Files
TransPyC/includes/llvmlite/__builder.py
2026-07-26 20:32:26 +08:00

1273 lines
50 KiB
Python
Raw Permalink 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 string
import viperlib
import memhub
import stdio
from linkedlist import GSListNode, GSList
from .__types import LLVMType, TypePrint
from .__values import Value, new_value, SSAValue
from .__function import Function, BasicBlock, block_append_text, function_move_block_to_end, _ll_name_needs_quote
# ============================================================
# IRBuilder: 游标式 IR 指令发射器
#
# 跟踪当前 BasicBlock将指令文本追加到块的指令链表。
# 维护递增的 SSA 名计数器(%0, %1, %2, ...)。
#
# 用法:
# builder = new_builder(pool, func)
# builder.PositionAtEnd(entry_block)
# a = builder.build_alloca(i32_ty)
# b = builder.build_alloca(i32_ty)
# builder.build_store(ConstInt(42), a)
# r = builder.build_load(i32_ty, a)
# builder.build_ret(r)
#
# 注意: 加 @t.NoVTable 以与硬编码 BUILDER_SIZE=32 保持一致
# (所有方法均为顶层函数,无需 vtable
# ============================================================
@t.NoVTable
class IRBuilder:
Pool: memhub.MemBuddy | t.CPtr # 内存池
Func: Function | t.CPtr # 所属函数
CurBlock: BasicBlock | t.CPtr # 当前插入点
Counter: t.CInt # SSA 名计数器
# ============================================================
# 工厂函数
# ============================================================
# IRBuilder 硬编码大小: Pool(8) + Func(8) + CurBlock(8) + Counter(4) = 32 字节
BUILDER_SIZE: t.CDefine = 32
def new_builder(pool: memhub.MemBuddy | t.CPtr, func: Function | t.CPtr) -> IRBuilder | t.CPtr:
"""创建 IRBuilder关联到函数 func"""
ptr: IRBuilder | t.CPtr = pool.alloc(BUILDER_SIZE)
if ptr is None: return None
string.memset(ptr, 0, BUILDER_SIZE)
ptr.Pool = pool
ptr.Func = func
ptr.CurBlock = None
ptr.Counter = 0
return ptr
# ============================================================
# 块定位
# ============================================================
def position_at_end(builder: IRBuilder | t.CPtr, block: BasicBlock | t.CPtr):
"""将插入点设为 block 的末尾,并把 block 移到函数块链表末尾。
移动 block 是为了保证 BB 文本输出顺序与控制流position顺序一致
避免 SSA 名在文本中非单调递增导致 llc 编译失败。
"""
if builder is None: return
builder.CurBlock = block
if builder.Func is not None:
function_move_block_to_end(builder.Func, block)
# ============================================================
# 内部辅助
# ============================================================
def _alloc_ssa_name(builder: IRBuilder | t.CPtr) -> t.CChar | t.CPtr:
"""分配一个 SSA 名字符串 "%N" 并递增计数器"""
pool: memhub.MemBuddy | t.CPtr = builder.Pool
buf: t.CChar | t.CPtr = pool.alloc(16)
if buf is None: return None
viperlib.snprintf(buf, 16, "%%%d", builder.Counter)
builder.Counter += 1
return buf
def _type_str(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr) -> t.CChar | t.CPtr:
"""获取类型的 IR 文本(从 pool 分配 512 字节缓冲区)
注意:复杂结构体类型(如 13 方法的虚表 {i8*,i8*,...})可能超过 64 字节,
必须使用足够大的缓冲区,否则 '}' 会被截断导致 IR 语法错误。
"""
pool: memhub.MemBuddy | t.CPtr = builder.Pool
buf: t.CChar | t.CPtr = pool.alloc(512)
if buf is None: return None
buf[0] = '\0'
TypePrint(buf, 512, ty, pool)
return buf
def _emit(builder: IRBuilder | t.CPtr, text: t.CChar | t.CPtr):
"""将指令文本追加到当前块"""
if builder is None or builder.CurBlock is None: return
block_append_text(builder.Pool, builder.CurBlock, text)
def _mark_terminated(builder: IRBuilder | t.CPtr):
"""标记当前块已终止"""
if builder is None or builder.CurBlock is None: return
builder.CurBlock.IsTerminated = 1
def builder_cur_block_is_terminated(builder: IRBuilder | t.CPtr) -> t.CInt:
"""检查当前块的 IsTerminated 标志
供其他模块绕过 stub 类型限制使用:
builder.CurBlock.IsTerminated 在 stub 类型中 BasicBlock 为 opaque
直接访问会被 TransPyC 静默跳过(不生成 GEP不报错
导致返回寄存器残留值br 回边指令被错误跳过。
"""
if builder is None or builder.CurBlock is None:
return 1
return builder.CurBlock.IsTerminated
# ============================================================
# 内存指令
# ============================================================
def build_alloca(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = alloca <ty>
返回类型为 ty* 的 SSA 值
"""
if builder is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ty_s: t.CChar | t.CPtr = _type_str(builder, ty)
line: t.CChar | t.CPtr = pool.alloc(128)
if line is None: return None
viperlib.snprintf(line, 128, "%s = alloca %s", name, ty_s)
_emit(builder, line)
# 结果类型是 ty*
result_ty: LLVMType | t.CPtr = LLVMType.Ptr(ty)
result_ty_ptr: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__())
if result_ty_ptr is not None:
string.memset(result_ty_ptr, 0, LLVMType.__sizeof__())
c.DerefAs(result_ty_ptr, result_ty)
return SSAValue(pool, result_ty_ptr, name)
def build_alloca_at_entry(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = alloca <ty>(在函数入口块生成,确保支配性)
在入口块的终止指令之前插入 alloca避免在条件分支内生成 alloca
导致的支配性违规Instruction does not dominate all uses
"""
if builder is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
func: Function | t.CPtr = builder.Func
if func is None or func.Blocks is None:
return build_alloca(builder, ty)
entry_blk: BasicBlock | t.CPtr = func.Blocks.Head
if entry_blk is None:
return build_alloca(builder, ty)
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ty_s: t.CChar | t.CPtr = _type_str(builder, ty)
line_text: t.CChar | t.CPtr = pool.alloc(128)
if line_text is None: return None
viperlib.snprintf(line_text, 128, "%s = alloca %s", name, ty_s)
# 在入口块终止指令之前插入
block_insert_text_before_terminator(pool, entry_blk, line_text)
# 结果类型是 ty*
result_ty: LLVMType | t.CPtr = LLVMType.Ptr(ty)
result_ty_ptr: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__())
if result_ty_ptr is not None:
string.memset(result_ty_ptr, 0, LLVMType.__sizeof__())
c.DerefAs(result_ty_ptr, result_ty)
return SSAValue(pool, result_ty_ptr, name)
def build_load(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr,
ptr: Value | t.CPtr) -> Value | t.CPtr:
"""%N = load <ty>, <ptr_ty> <ptr>"""
if builder is None or ptr is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ty_s: t.CChar | t.CPtr = _type_str(builder, ty)
ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = load %s, %s %s", name, ty_s, ptr_ty_s, ptr.Name)
_emit(builder, line)
return SSAValue(pool, ty, name)
def build_store(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, ptr: Value | t.CPtr):
"""store <val_ty> <val>, <ptr_ty> <ptr>"""
if builder is None or val is None or ptr is None: return
pool: memhub.MemBuddy | t.CPtr = builder.Pool
val_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return
viperlib.snprintf(line, 256, "store %s %s, %s %s", val_ty_s, val.Name, ptr_ty_s, ptr.Name)
_emit(builder, line)
# ============================================================
# 二元运算指令
# ============================================================
def build_add(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = add <ty> <lhs>, <rhs>"""
return _build_binop(builder, "add", lhs, rhs)
def build_sub(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = sub <ty> <lhs>, <rhs>"""
return _build_binop(builder, "sub", lhs, rhs)
def build_mul(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = mul <ty> <lhs>, <rhs>"""
return _build_binop(builder, "mul", lhs, rhs)
def build_sdiv(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = sdiv <ty> <lhs>, <rhs>"""
return _build_binop(builder, "sdiv", lhs, rhs)
def build_udiv(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = udiv <ty> <lhs>, <rhs>"""
return _build_binop(builder, "udiv", lhs, rhs)
def build_srem(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = srem <ty> <lhs>, <rhs>"""
return _build_binop(builder, "srem", lhs, rhs)
def build_urem(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = urem <ty> <lhs>, <rhs>"""
return _build_binop(builder, "urem", lhs, rhs)
def build_and(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = and <ty> <lhs>, <rhs>"""
return _build_binop(builder, "and", lhs, rhs)
def build_or(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = or <ty> <lhs>, <rhs>"""
return _build_binop(builder, "or", lhs, rhs)
def build_xor(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = xor <ty> <lhs>, <rhs>"""
return _build_binop(builder, "xor", lhs, rhs)
def build_shl(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = shl <ty> <lhs>, <rhs>"""
return _build_binop(builder, "shl", lhs, rhs)
def build_lshr(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = lshr <ty> <lhs>, <rhs>"""
return _build_binop(builder, "lshr", lhs, rhs)
def build_ashr(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = ashr <ty> <lhs>, <rhs>"""
return _build_binop(builder, "ashr", lhs, rhs)
# ============================================================
# 浮点二元运算指令
# ============================================================
def build_fadd(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = fadd <ty> <lhs>, <rhs>"""
return _build_binop(builder, "fadd", lhs, rhs)
def build_fsub(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = fsub <ty> <lhs>, <rhs>"""
return _build_binop(builder, "fsub", lhs, rhs)
def build_fmul(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = fmul <ty> <lhs>, <rhs>"""
return _build_binop(builder, "fmul", lhs, rhs)
def build_fdiv(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = fdiv <ty> <lhs>, <rhs>"""
return _build_binop(builder, "fdiv", lhs, rhs)
def build_frem(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr,
rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = frem <ty> <lhs>, <rhs>"""
return _build_binop(builder, "frem", lhs, rhs)
def build_fneg(builder: IRBuilder | t.CPtr, val: Value | t.CPtr) -> Value | t.CPtr:
"""%N = fneg <ty> <val>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
line: t.CChar | t.CPtr = pool.alloc(128)
if line is None: return None
viperlib.snprintf(line, 128, "%s = fneg %s %s", name, ty_s, val.Name)
_emit(builder, line)
return SSAValue(pool, val.Ty, name)
def _build_binop(builder: IRBuilder | t.CPtr, op: t.CChar | t.CPtr,
lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr:
"""二元运算通用实现"""
if builder is None or lhs is None or rhs is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ty_s: t.CChar | t.CPtr = _type_str(builder, lhs.Ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = %s %s %s, %s", name, op, ty_s, lhs.Name, rhs.Name)
_emit(builder, line)
return SSAValue(pool, lhs.Ty, name)
# ============================================================
# 比较指令
# ============================================================
# ICMP 谓词常量
ICMP_EQ: t.CDefine = 0
ICMP_NE: t.CDefine = 1
ICMP_SGT: t.CDefine = 2
ICMP_SGE: t.CDefine = 3
ICMP_SLT: t.CDefine = 4
ICMP_SLE: t.CDefine = 5
ICMP_UGT: t.CDefine = 6
ICMP_UGE: t.CDefine = 7
ICMP_ULT: t.CDefine = 8
ICMP_ULE: t.CDefine = 9
# FCMP 谓词常量
FCMP_FALSE: t.CDefine = 0
FCMP_OEQ: t.CDefine = 1
FCMP_OGT: t.CDefine = 2
FCMP_OGE: t.CDefine = 3
FCMP_OLT: t.CDefine = 4
FCMP_OLE: t.CDefine = 5
FCMP_ONE: t.CDefine = 6
FCMP_ORD: t.CDefine = 7
FCMP_UNO: t.CDefine = 8
FCMP_UEQ: t.CDefine = 9
FCMP_UGT: t.CDefine = 10
FCMP_UGE: t.CDefine = 11
FCMP_ULT: t.CDefine = 12
FCMP_ULE: t.CDefine = 13
FCMP_UNE: t.CDefine = 14
FCMP_TRUE: t.CDefine = 15
def _icmp_pred_str(pred: t.CInt) -> t.CChar | t.CPtr:
if pred == ICMP_EQ: return "eq"
if pred == ICMP_NE: return "ne"
if pred == ICMP_SGT: return "sgt"
if pred == ICMP_SGE: return "sge"
if pred == ICMP_SLT: return "slt"
if pred == ICMP_SLE: return "sle"
if pred == ICMP_UGT: return "ugt"
if pred == ICMP_UGE: return "uge"
if pred == ICMP_ULT: return "ult"
if pred == ICMP_ULE: return "ule"
return "eq"
def _fcmp_pred_str(pred: t.CInt) -> t.CChar | t.CPtr:
if pred == FCMP_FALSE: return "false"
if pred == FCMP_OEQ: return "oeq"
if pred == FCMP_OGT: return "ogt"
if pred == FCMP_OGE: return "oge"
if pred == FCMP_OLT: return "olt"
if pred == FCMP_OLE: return "ole"
if pred == FCMP_ONE: return "one"
if pred == FCMP_ORD: return "ord"
if pred == FCMP_UNO: return "uno"
if pred == FCMP_UEQ: return "ueq"
if pred == FCMP_UGT: return "ugt"
if pred == FCMP_UGE: return "uge"
if pred == FCMP_ULT: return "ult"
if pred == FCMP_ULE: return "ule"
if pred == FCMP_UNE: return "une"
if pred == FCMP_TRUE: return "true"
return "oeq"
def build_icmp(builder: IRBuilder | t.CPtr, pred: t.CInt,
lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = icmp <pred> <ty> <lhs>, <rhs>
返回类型为 i1 的 SSA 值
"""
if builder is None or lhs is None or rhs is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ty_s: t.CChar | t.CPtr = _type_str(builder, lhs.Ty)
pred_s: t.CChar | t.CPtr = _icmp_pred_str(pred)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = icmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name)
_emit(builder, line)
# 结果类型是 i1
i1_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__())
if i1_ty is not None:
string.memset(i1_ty, 0, LLVMType.__sizeof__())
c.DerefAs(i1_ty, LLVMType.Int(1))
return SSAValue(pool, i1_ty, name)
def build_fcmp(builder: IRBuilder | t.CPtr, pred: t.CInt,
lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr:
"""%N = fcmp <pred> <ty> <lhs>, <rhs>
返回类型为 i1 的 SSA 值
"""
if builder is None or lhs is None or rhs is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ty_s: t.CChar | t.CPtr = _type_str(builder, lhs.Ty)
pred_s: t.CChar | t.CPtr = _fcmp_pred_str(pred)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = fcmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name)
_emit(builder, line)
# 结果类型是 i1
i1_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__())
if i1_ty is not None:
string.memset(i1_ty, 0, LLVMType.__sizeof__())
c.DerefAs(i1_ty, LLVMType.Int(1))
return SSAValue(pool, i1_ty, name)
# ============================================================
# 终止指令
# ============================================================
def build_br(builder: IRBuilder | t.CPtr, target: BasicBlock | t.CPtr):
"""br label %<target>"""
if builder is None or target is None: return
pool: memhub.MemBuddy | t.CPtr = builder.Pool
line: t.CChar | t.CPtr = pool.alloc(64)
if line is None: return
viperlib.snprintf(line, 64, "br label %%%s", target.Name)
_emit(builder, line)
_mark_terminated(builder)
def build_cond_br(builder: IRBuilder | t.CPtr, cond: Value | t.CPtr,
then_blk: BasicBlock | t.CPtr, else_blk: BasicBlock | t.CPtr):
"""br i1 <cond>, label %<then>, label %<else_>"""
if builder is None or cond is None or then_blk is None or else_blk is None: return
pool: memhub.MemBuddy | t.CPtr = builder.Pool
line: t.CChar | t.CPtr = pool.alloc(128)
if line is None: return
viperlib.snprintf(line, 128, "br i1 %s, label %%%s, label %%%s",
cond.Name, then_blk.Name, else_blk.Name)
_emit(builder, line)
_mark_terminated(builder)
def build_ret(builder: IRBuilder | t.CPtr, val: Value | t.CPtr):
"""ret <ty> <val>"""
if builder is None or val is None: return
pool: memhub.MemBuddy | t.CPtr = builder.Pool
ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
line: t.CChar | t.CPtr = pool.alloc(128)
if line is None: return
viperlib.snprintf(line, 128, "ret %s %s", ty_s, val.Name)
_emit(builder, line)
_mark_terminated(builder)
def build_ret_void(builder: IRBuilder | t.CPtr):
"""ret void"""
if builder is None: return
pool: memhub.MemBuddy | t.CPtr = builder.Pool
line: t.CChar | t.CPtr = pool.alloc(16)
if line is None: return
string.strcpy(line, "ret void")
_emit(builder, line)
_mark_terminated(builder)
# ============================================================
# 调用指令
# ============================================================
def build_call(builder: IRBuilder | t.CPtr, callee: t.CChar | t.CPtr,
args: Value | t.CPtr, arg_count: t.CInt,
ret_ty: LLVMType | t.CPtr,
is_variadic: t.CInt = 0) -> Value | t.CPtr:
"""%N = call <ret_ty> @<callee>(<args>)
args 是 Value 链表头arg_count 是参数数量。
若 ret_ty 是 void不分配 SSA 名(使用 call 而非 %N = call
is_variadic=1 时发射函数类型签名(如 i32 (i8*, ...) @printf
"""
if builder is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
# 判断返回类型是否为 void
is_void: t.CInt = 0
match ret_ty:
case LLVMType.Void():
is_void = 1
case _:
is_void = 0
name: t.CChar | t.CPtr = None
if is_void == 0:
name = _alloc_ssa_name(builder)
ret_ty_s: t.CChar | t.CPtr = _type_str(builder, ret_ty)
# 构建参数列表文本 + 变参函数类型签名
args_buf: t.CChar | t.CPtr = pool.alloc(1024)
if args_buf is None: return None
args_buf[0] = '\0'
func_ty_buf: t.CChar | t.CPtr = pool.alloc(1024)
if func_ty_buf is None: return None
func_ty_buf[0] = '\0'
cur: Value | t.CPtr = args
first: t.CInt = 1
while cur is not None:
if first == 0:
_append_cstr(args_buf, 1024, ", ")
_append_cstr(func_ty_buf, 1024, ", ")
arg_ty_s: t.CChar | t.CPtr = _type_str(builder, cur.Ty)
_append_cstr(args_buf, 1024, arg_ty_s)
_append_cstr(args_buf, 1024, " ")
if cur.Name is not None:
_append_cstr(args_buf, 1024, cur.Name)
_append_cstr(func_ty_buf, 1024, arg_ty_s)
cur = cur.Next
first = 0
if is_variadic == 1:
if first == 0:
_append_cstr(func_ty_buf, 1024, ", ")
_append_cstr(func_ty_buf, 1024, "...")
# 构造带引号的 callee 名字SHA1 前缀名含 '.' 或以数字开头需要加引号)
callee_buf: t.CChar | t.CPtr = pool.alloc(128)
if callee_buf is None: return None
callee_buf[0] = '\0'
if _ll_name_needs_quote(callee) != 0:
_append_cstr(callee_buf, 128, "\"")
_append_cstr(callee_buf, 128, callee)
_append_cstr(callee_buf, 128, "\"")
else:
_append_cstr(callee_buf, 128, callee)
line: t.CChar | t.CPtr = pool.alloc(2048)
if line is None: return None
if is_variadic == 1:
if is_void == 1:
viperlib.snprintf(line, 2048, "call %s (%s) @%s(%s)", ret_ty_s, func_ty_buf, callee_buf, args_buf)
else:
viperlib.snprintf(line, 2048, "%s = call %s (%s) @%s(%s)", name, ret_ty_s, func_ty_buf, callee_buf, args_buf)
else:
if is_void == 1:
viperlib.snprintf(line, 2048, "call %s @%s(%s)", ret_ty_s, callee_buf, args_buf)
else:
viperlib.snprintf(line, 2048, "%s = call %s @%s(%s)", name, ret_ty_s, callee_buf, args_buf)
_emit(builder, line)
if is_void == 1:
return None
return SSAValue(pool, ret_ty, name)
# ============================================================
# 间接调用指令: call <ret_ty> %callee(<args>)
#
# callee_val 是函数指针 SSA 值(必须已是函数指针类型,如 i32(i8*)*
# ============================================================
def build_call_indirect(builder: IRBuilder | t.CPtr, callee_val: Value | t.CPtr,
args: Value | t.CPtr, arg_count: t.CInt,
ret_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = call <ret_ty> %callee(<args>)
间接调用callee_val 是函数指针 SSA 值。
"""
if builder is None or callee_val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
is_void: t.CInt = 0
match ret_ty:
case LLVMType.Void():
is_void = 1
case _:
is_void = 0
name: t.CChar | t.CPtr = None
if is_void == 0:
name = _alloc_ssa_name(builder)
ret_ty_s: t.CChar | t.CPtr = _type_str(builder, ret_ty)
# 构建参数列表文本
args_buf: t.CChar | t.CPtr = pool.alloc(1024)
if args_buf is None: return None
args_buf[0] = '\0'
cur: Value | t.CPtr = args
first: t.CInt = 1
while cur is not None:
if first == 0:
_append_cstr(args_buf, 1024, ", ")
arg_ty_s: t.CChar | t.CPtr = _type_str(builder, cur.Ty)
_append_cstr(args_buf, 1024, arg_ty_s)
_append_cstr(args_buf, 1024, " ")
if cur.Name is not None:
_append_cstr(args_buf, 1024, cur.Name)
cur = cur.Next
first = 0
line: t.CChar | t.CPtr = pool.alloc(2048)
if line is None: return None
if is_void == 1:
viperlib.snprintf(line, 2048, "call %s %s(%s)", ret_ty_s, callee_val.Name, args_buf)
else:
viperlib.snprintf(line, 2048, "%s = call %s %s(%s)", name, ret_ty_s, callee_val.Name, args_buf)
_emit(builder, line)
if is_void == 1:
return None
return SSAValue(pool, ret_ty, name)
# ============================================================
# 类型转换指令
# ============================================================
def build_bitcast(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = bitcast <from_ty> <val> to <to_ty>"""
if builder is None or val is None:
return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
if val.Name is None:
return None
viperlib.snprintf(line, 256, "%s = bitcast %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
ret: Value | t.CPtr = SSAValue(pool, to_ty, name)
return ret
def build_sext(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = sext <from_ty> <val> to <to_ty>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = sext %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
return SSAValue(pool, to_ty, name)
def build_trunc(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = trunc <from_ty> <val> to <to_ty>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = trunc %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
return SSAValue(pool, to_ty, name)
def build_zext(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = zext <from_ty> <val> to <to_ty>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = zext %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
return SSAValue(pool, to_ty, name)
def build_ptrtoint(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = ptrtoint <from_ty> <val> to <to_ty>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = ptrtoint %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
return SSAValue(pool, to_ty, name)
def build_inttoptr(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = inttoptr <from_ty> <val> to <to_ty>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = inttoptr %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
return SSAValue(pool, to_ty, name)
# ============================================================
# 浮点转换指令
# ============================================================
def build_fpext(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = fpext <from_ty> <val> to <to_ty>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = fpext %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
return SSAValue(pool, to_ty, name)
def build_fptrunc(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = fptrunc <from_ty> <val> to <to_ty>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = fptrunc %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
return SSAValue(pool, to_ty, name)
def build_fp2si(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = fptosi <from_ty> <val> to <to_ty>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = fptosi %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
return SSAValue(pool, to_ty, name)
def build_si2fp(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = sitofp <from_ty> <val> to <to_ty>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = sitofp %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
return SSAValue(pool, to_ty, name)
def build_fp2ui(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = fptoui <from_ty> <val> to <to_ty>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = fptoui %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
return SSAValue(pool, to_ty, name)
def build_ui2fp(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
to_ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = uitofp <from_ty> <val> to <to_ty>"""
if builder is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = uitofp %s %s to %s", name, from_ty_s, val.Name, to_ty_s)
_emit(builder, line)
return SSAValue(pool, to_ty, name)
# ============================================================
# GEP 指令
# ============================================================
def build_gep(builder: IRBuilder | t.CPtr, elem_ty: LLVMType | t.CPtr,
ptr: Value | t.CPtr, idx: Value | t.CPtr) -> Value | t.CPtr:
"""%N = getelementptr <elem_ty>, <ptr_ty> <ptr>, <idx_ty> <idx>"""
if builder is None or ptr is None or idx is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
elem_ty_s: t.CChar | t.CPtr = _type_str(builder, elem_ty)
ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty)
idx_ty_s: t.CChar | t.CPtr = _type_str(builder, idx.Ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = getelementptr %s, %s %s, %s %s",
name, elem_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name)
_emit(builder, line)
# GEP 结果类型是 elem_ty*
result_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__())
if result_ty is not None:
string.memset(result_ty, 0, LLVMType.__sizeof__())
c.DerefAs(result_ty, LLVMType.Ptr(elem_ty))
return SSAValue(pool, result_ty, name)
# ============================================================
# build_gep_array — 数组双索引 GEP: getelementptr [N x ty], [N x ty]* %ptr, i32 0, i32 %idx
# ============================================================
def build_gep_array(builder: IRBuilder | t.CPtr, array_ty: LLVMType | t.CPtr,
elem_ty: LLVMType | t.CPtr,
ptr: Value | t.CPtr, idx: Value | t.CPtr) -> Value | t.CPtr:
"""%N = getelementptr [N x elem_ty], [N x elem_ty]* %ptr, i32 0, i32 %idx
结果类型是 elem_ty*
"""
if builder is None or ptr is None or idx is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
array_ty_s: t.CChar | t.CPtr = _type_str(builder, array_ty)
ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty)
idx_ty_s: t.CChar | t.CPtr = _type_str(builder, idx.Ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = getelementptr %s, %s %s, i32 0, %s %s",
name, array_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name)
_emit(builder, line)
# GEP 结果类型是 elem_ty*
result_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__())
if result_ty is not None:
string.memset(result_ty, 0, LLVMType.__sizeof__())
c.DerefAs(result_ty, LLVMType.Ptr(elem_ty))
return SSAValue(pool, result_ty, name)
# ============================================================
# build_gep_struct — 结构体字段 GEP: getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx
# ============================================================
def build_gep_struct(builder: IRBuilder | t.CPtr, struct_ty: LLVMType | t.CPtr,
field_ty: LLVMType | t.CPtr,
ptr: Value | t.CPtr, field_idx: t.CInt) -> Value | t.CPtr:
"""%N = getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx
结果类型是 field_ty*
"""
if builder is None or ptr is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
struct_ty_s: t.CChar | t.CPtr = _type_str(builder, struct_ty)
ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty)
line: t.CChar | t.CPtr = pool.alloc(2048)
if line is None:
stdio.printf("[BGS] line alloc None\n")
stdio.fflush(0)
return None
viperlib.snprintf(line, 2048, "%s = getelementptr %s, %s %s, i32 0, i32 %d",
name, struct_ty_s, ptr_ty_s, ptr.Name, field_idx)
_emit(builder, line)
# GEP 结果类型是 field_ty*
result_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__())
if result_ty is None:
stdio.printf("[BGS] result_ty alloc None\n")
stdio.fflush(0)
else:
string.memset(result_ty, 0, LLVMType.__sizeof__())
c.DerefAs(result_ty, LLVMType.Ptr(field_ty))
rv: Value | t.CPtr = SSAValue(pool, result_ty, name)
if rv is None:
stdio.printf("[BGS] SSAValue None\n")
stdio.fflush(0)
return rv
# ============================================================
# PhiIncoming: phi 节点的入边 (value, block) 对
# 继承 GSListNode[PhiIncoming] 获取强类型 Next
#
# 注意: 必须加 @t.NoVTable理由见 __function.py 的 Line 注释)。
# ============================================================
@t.NoVTable
class PhiIncoming(GSListNode[PhiIncoming]):
Val: Value | t.CPtr
Block: BasicBlock | t.CPtr
# PhiIncoming 硬编码大小: Next(8) + Val(8) + Block(8) = 24 字节
PHIINC_SIZE: t.CDefine = 24
def new_phi_incoming(pool: memhub.MemBuddy | t.CPtr,
val: Value | t.CPtr, block: BasicBlock | t.CPtr) -> PhiIncoming | t.CPtr:
ptr: PhiIncoming | t.CPtr = pool.alloc(PHIINC_SIZE)
if ptr is None: return None
string.memset(ptr, 0, PHIINC_SIZE)
ptr.Val = val
ptr.Block = block
return ptr
# ============================================================
# Phi 指令
# ============================================================
def build_phi(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr,
incoming_head: PhiIncoming | t.CPtr, count: t.CInt) -> Value | t.CPtr:
"""%N = phi <ty> [val1, %blk1], [val2, %blk2], ...
incoming_head 是 PhiIncoming 链表头count 是入边数量。
"""
if builder is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ty_s: t.CChar | t.CPtr = _type_str(builder, ty)
# 构建入边列表文本: [val, %blk], [val, %blk], ...
# 入边可能很多(如 30+ 个 or 链的 bool.merge需要大缓冲区
parts: t.CChar | t.CPtr = pool.alloc(4096)
if parts is None: return None
parts[0] = '\0'
cur: PhiIncoming | t.CPtr = incoming_head
first: t.CInt = 1
while cur is not None:
if first == 0:
_append_cstr(parts, 4096, ", ")
_append_cstr(parts, 4096, "[")
# 使用中间变量避免链式结构体成员访问被编译器丢弃
inc_val: Value | t.CPtr = cur.Val
if inc_val is not None:
inc_val_name: t.CChar | t.CPtr = inc_val.Name
if inc_val_name is not None:
_append_cstr(parts, 4096, inc_val_name)
_append_cstr(parts, 4096, ", %")
inc_blk: BasicBlock | t.CPtr = cur.Block
if inc_blk is not None:
inc_blk_name: t.CChar | t.CPtr = inc_blk.Name
if inc_blk_name is not None:
_append_cstr(parts, 4096, inc_blk_name)
_append_cstr(parts, 4096, "]")
cur = cur.Next
first = 0
line: t.CChar | t.CPtr = pool.alloc(8192)
if line is None: return None
viperlib.snprintf(line, 8192, "%s = phi %s %s", name, ty_s, parts)
_emit(builder, line)
return SSAValue(pool, ty, name)
# ============================================================
# SwitchCase: switch 指令的 case 分支
#
# 注意: 必须加 @t.NoVTable理由见 __function.py 的 Line 注释)。
# ============================================================
@t.NoVTable
class SwitchCase(GSListNode[SwitchCase]):
CaseVal: Value | t.CPtr
Block: BasicBlock | t.CPtr
# SwitchCase 硬编码大小: Next(8) + CaseVal(8) + Block(8) = 24 字节
SWCASE_SIZE: t.CDefine = 24
def new_switch_case(pool: memhub.MemBuddy | t.CPtr,
case_val: Value | t.CPtr, block: BasicBlock | t.CPtr) -> SwitchCase | t.CPtr:
ptr: SwitchCase | t.CPtr = pool.alloc(SWCASE_SIZE)
if ptr is None: return None
string.memset(ptr, 0, SWCASE_SIZE)
ptr.CaseVal = case_val
ptr.Block = block
return ptr
# ============================================================
# Switch 指令
# ============================================================
def build_switch(builder: IRBuilder | t.CPtr, val: Value | t.CPtr,
default_blk: BasicBlock | t.CPtr,
cases_head: SwitchCase | t.CPtr, case_count: t.CInt):
"""switch <ty> <val>, label %<default> [<case_val>, label %<blk> ...]"""
if builder is None or val is None or default_blk is None: return
pool: memhub.MemBuddy | t.CPtr = builder.Pool
ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
# 构建主体: switch <ty> <val>, label %default
line: t.CChar | t.CPtr = pool.alloc(512)
if line is None: return
viperlib.snprintf(line, 512, "switch %s %s, label %%%s",
ty_s, val.Name, default_blk.Name)
_append_cstr(line, 512, " [")
_append_cstr(line, 512, "\n")
# 遍历 case 列表
cur: SwitchCase | t.CPtr = cases_head
case_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
while cur is not None:
case_line: t.CChar | t.CPtr = pool.alloc(128)
if case_line is not None:
# 使用中间变量避免链式结构体成员访问被编译器丢弃
cv: Value | t.CPtr = cur.CaseVal
cb: BasicBlock | t.CPtr = cur.Block
if cv is not None and cb is not None:
cv_name: t.CChar | t.CPtr = cv.Name
cb_name: t.CChar | t.CPtr = cb.Name
if cv_name is not None and cb_name is not None:
viperlib.snprintf(case_line, 128, " %s %s, label %%%s\n",
case_ty_s, cv_name, cb_name)
_append_cstr(line, 512, case_line)
cur = cur.Next
_append_cstr(line, 512, "]")
_emit(builder, line)
_mark_terminated(builder)
# ============================================================
# Select 指令
# ============================================================
def build_select(builder: IRBuilder | t.CPtr, cond: Value | t.CPtr,
true_val: Value | t.CPtr, false_val: Value | t.CPtr) -> Value | t.CPtr:
"""%N = select i1 <cond>, <ty> <true_val>, <ty> <false_val>"""
if builder is None or cond is None or true_val is None or false_val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ty_s: t.CChar | t.CPtr = _type_str(builder, true_val.Ty)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = select i1 %s, %s %s, %s %s",
name, cond.Name, ty_s, true_val.Name, ty_s, false_val.Name)
_emit(builder, line)
return SSAValue(pool, true_val.Ty, name)
# ============================================================
# Unreachable 指令
# ============================================================
def build_unreachable(builder: IRBuilder | t.CPtr):
"""unreachable"""
if builder is None: return
pool: memhub.MemBuddy | t.CPtr = builder.Pool
line: t.CChar | t.CPtr = pool.alloc(16)
if line is None: return
string.strcpy(line, "unreachable")
_emit(builder, line)
_mark_terminated(builder)
# ============================================================
# 原子指令 (Phase 3)
# ============================================================
# AtomicRMW 操作常量
ATOMIC_XCHG: t.CDefine = 0
ATOMIC_ADD: t.CDefine = 1
ATOMIC_SUB: t.CDefine = 2
ATOMIC_AND: t.CDefine = 3
ATOMIC_NAND: t.CDefine = 4
ATOMIC_OR: t.CDefine = 5
ATOMIC_XOR: t.CDefine = 6
ATOMIC_MAX: t.CDefine = 7
ATOMIC_MIN: t.CDefine = 8
ATOMIC_UMAX: t.CDefine = 9
ATOMIC_UMIN: t.CDefine = 10
# 内存序常量
ATOMIC_ORDER_NOTATOMIC: t.CDefine = 0
ATOMIC_ORDER_UNORDERED: t.CDefine = 1
ATOMIC_ORDER_MONOTONIC: t.CDefine = 2
ATOMIC_ORDER_ACQUIRE: t.CDefine = 3
ATOMIC_ORDER_RELEASE: t.CDefine = 4
ATOMIC_ORDER_ACQ_REL: t.CDefine = 5
ATOMIC_ORDER_SEQ_CST: t.CDefine = 6
def _atomic_op_str(op: t.CInt) -> t.CChar | t.CPtr:
if op == ATOMIC_XCHG: return "xchg"
if op == ATOMIC_ADD: return "add"
if op == ATOMIC_SUB: return "sub"
if op == ATOMIC_AND: return "and"
if op == ATOMIC_NAND: return "nand"
if op == ATOMIC_OR: return "or"
if op == ATOMIC_XOR: return "xor"
if op == ATOMIC_MAX: return "max"
if op == ATOMIC_MIN: return "min"
if op == ATOMIC_UMAX: return "umax"
if op == ATOMIC_UMIN: return "umin"
return "xchg"
def _atomic_order_str(ord: t.CInt) -> t.CChar | t.CPtr:
if ord == ATOMIC_ORDER_UNORDERED: return "unordered"
if ord == ATOMIC_ORDER_MONOTONIC: return "monotonic"
if ord == ATOMIC_ORDER_ACQUIRE: return "acquire"
if ord == ATOMIC_ORDER_RELEASE: return "release"
if ord == ATOMIC_ORDER_ACQ_REL: return "acq_rel"
if ord == ATOMIC_ORDER_SEQ_CST: return "seq_cst"
return "monotonic"
def build_atomicrmw(builder: IRBuilder | t.CPtr, op: t.CInt,
ptr: Value | t.CPtr, val: Value | t.CPtr,
order: t.CInt) -> Value | t.CPtr:
"""%N = atomicrmw <op> <ptr_ty> <ptr>, <val_ty> <val> <ordering>"""
if builder is None or ptr is None or val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty)
val_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty)
op_s: t.CChar | t.CPtr = _atomic_op_str(op)
ord_s: t.CChar | t.CPtr = _atomic_order_str(order)
line: t.CChar | t.CPtr = pool.alloc(256)
if line is None: return None
viperlib.snprintf(line, 256, "%s = atomicrmw %s %s %s, %s %s %s",
name, op_s, ptr_ty_s, ptr.Name, val_ty_s, val.Name, ord_s)
_emit(builder, line)
return SSAValue(pool, val.Ty, name)
def build_cmpxchg(builder: IRBuilder | t.CPtr, ptr: Value | t.CPtr,
cmp_val: Value | t.CPtr, new_val: Value | t.CPtr,
succ_order: t.CInt, fail_order: t.CInt) -> Value | t.CPtr:
"""%N = cmpxchg <ptr_ty> <ptr>, <cmp_ty> <cmp>, <new_ty> <new> <succ_order> <fail_order>
返回 {<ty>, i1} 类型的 SSA 值(简化为返回 i64 表示的值对)
"""
if builder is None or ptr is None or cmp_val is None or new_val is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty)
cmp_ty_s: t.CChar | t.CPtr = _type_str(builder, cmp_val.Ty)
new_ty_s: t.CChar | t.CPtr = _type_str(builder, new_val.Ty)
succ_s: t.CChar | t.CPtr = _atomic_order_str(succ_order)
fail_s: t.CChar | t.CPtr = _atomic_order_str(fail_order)
line: t.CChar | t.CPtr = pool.alloc(384)
if line is None: return None
viperlib.snprintf(line, 384, "%s = cmpxchg %s %s, %s %s, %s %s %s %s",
name, ptr_ty_s, ptr.Name, cmp_ty_s, cmp_val.Name,
new_ty_s, new_val.Name, succ_s, fail_s)
_emit(builder, line)
# 返回原始值的类型
return SSAValue(pool, new_val.Ty, name)
# ============================================================
# 内联汇编 (Phase 3)
# ============================================================
def build_inline_asm(builder: IRBuilder | t.CPtr, ret_ty: LLVMType | t.CPtr,
asm_text: t.CChar | t.CPtr, constraints: t.CChar | t.CPtr,
has_side_effects: t.CInt) -> Value | t.CPtr:
"""%N = call <ret_ty> asm [sideeffect] "" "<constraints>"()
简化的内联汇编调用,无参数版本。
"""
if builder is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
# 判断返回类型是否为 void
is_void: t.CInt = 0
match ret_ty:
case LLVMType.Void():
is_void = 1
case _:
is_void = 0
name: t.CChar | t.CPtr = None
if is_void == 0:
name = _alloc_ssa_name(builder)
ret_ty_s: t.CChar | t.CPtr = _type_str(builder, ret_ty)
line: t.CChar | t.CPtr = pool.alloc(512)
if line is None: return None
if has_side_effects != 0:
if is_void == 1:
viperlib.snprintf(line, 512, "call %s asm sideeffect \"%s\", \"%s\"()",
ret_ty_s, asm_text, constraints)
else:
viperlib.snprintf(line, 512, "%s = call %s asm sideeffect \"%s\", \"%s\"()",
name, ret_ty_s, asm_text, constraints)
else:
if is_void == 1:
viperlib.snprintf(line, 512, "call %s asm \"%s\", \"%s\"()",
ret_ty_s, asm_text, constraints)
else:
viperlib.snprintf(line, 512, "%s = call %s asm \"%s\", \"%s\"()",
name, ret_ty_s, asm_text, constraints)
_emit(builder, line)
if is_void == 1:
return None
return SSAValue(pool, ret_ty, name)
# ============================================================
# 内部辅助
# ============================================================
def _append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPtr):
"""将 C 字符串 src 追加到 dst 末尾"""
if dst is None or src is None: return
dlen: t.CSizeT = string.strlen(dst)
slen: t.CSizeT = string.strlen(src)
remain: t.CSizeT = dst_size - dlen
if remain <= 0: return
i: t.CSizeT = 0
while i < slen and i + 1 < remain:
dst[dlen + i] = src[i]
i += 1
dst[dlen + i] = '\0'