Files
TransPyC/includes/llvmlite/__function.vp
T
TermiNexus d2d319eb21 fix(includes): ASan 集成支持 + 越界写封堵 + TypePrint 类型名截断 40→255
1. Phase2Translator 增加 TPC_ASAN 支持(opt asan 插桩+链接 -fsanitize=address),配合 clang64 定位 0xbe 崩溃。2. llvmlite/memhub/viperio/viperlib 多处理无界 strcpy/strcat/下溢/递归守卫/栈数组钳制。3. memhub alloc 块尾 canary(MEMBUDDY_CANARY)+check_alive_blocks 校验+chk_stack realloc 扩容。4. TypePrint Ptr(Struct) 类型名截断上限 40→255:含 sha1 前缀类型名(如 bbdf3bbd4c3bc28c.CONSOLE_SCREEN_BUFFER_INFO=42)被截断产生未定义类型引用(llc undefined type)。
2026-08-10 18:09:13 +08:00

806 lines
30 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import string
import viperlib
import memhub
import stdio
from linkedlist import GSListNode, GSList
from .__types import LLVMType, ParamNode, TypePrint
# ============================================================
# Line: 指令文本行
#
# 每条 LLVM IR 指令序列化为一行文本,通过链表组织。
# 继承 GSListNode[Line] 获取强类型 Next: Line|CPtr
#
# 注意: 必须加 @t.NoVTable。否则 TransPyC 为子类添加 vtable 指针(T*),
# 但完全丢失父类 GSListNode[T] 的 Next 字段,导致 struct 布局错位,
# 所有字段访问读取垃圾值(见续 XV 会话根因分析)。
# ============================================================
@t.NoVTable
class Line(GSListNode[Line]):
Buf: t.CChar | t.CPtr # 行文本(NUL 结尾)
Len: t.CSizeT # 文本长度(不含 NUL)
# ============================================================
# BasicBlock: 基本块
#
# 持有名称 + 指令行链表 + 终止指令标志。
# 继承 GSListNode[BasicBlock] 获取强类型 Next: BasicBlock|CPtr
# Lines 为 GSList[Line] 指针,提供 O(1) 追加。
#
# 注意: 必须加 @t.NoVTable(理由同 Line)。
# ============================================================
@t.NoVTable
class BasicBlock(GSListNode[BasicBlock]):
Name: t.CChar | t.CPtr # 块名(如 "entry"/"if.then")
Lines: GSList[Line] | t.CPtr # 指令行链表容器(Head/Tail/Count)
IsTerminated: t.CInt # 1=已有终止指令(br/ret)
# ============================================================
# Param: 函数参数
#
# 继承 GSListNode[Param] 获取强类型 Next: Param|CPtr
# Attrs 存储参数属性文本(如 "noundef"/"nocapture"/"readonly"),None=无属性
#
# 注意: 必须加 @t.NoVTable(理由同 Line)。
# ============================================================
@t.NoVTable
class Param(GSListNode[Param]):
Ty: LLVMType | t.CPtr # 参数类型
Name: t.CChar | t.CPtr # 参数名(如 "%0"/"%a")
Attrs: t.CChar | t.CPtr # 参数属性文本(None=无属性)
# ============================================================
# Function: 函数
#
# 持有名称 + 返回类型 + 参数链表 + 基本块链表。
# 继承 GSListNode[Function] 获取强类型 Next: Function|CPtr
# Params/Blocks 为 GSList 指针,提供 O(1) 追加。
# Attrs 存储函数级属性文本(如 "nounwind"/"noinline"),None=无属性
#
# 注意: 必须加 @t.NoVTable(理由同 Line)。
# ============================================================
@t.NoVTable
class Function(GSListNode[Function]):
Name: t.CChar | t.CPtr # 函数名
RetTy: LLVMType | t.CPtr # 返回类型
Params: GSList[Param] | t.CPtr # 参数链表容器
Blocks: GSList[BasicBlock] | t.CPtr # 基本块链表容器
IsDeclared: t.CInt # 1=仅声明(declare), 0=定义(define)
IsVarArg: t.CInt # 1=变参函数(...)
Attrs: t.CChar | t.CPtr # 函数属性文本(None=无属性)
# ============================================================
# 工厂函数
# ============================================================
# Line 硬编码大小: Next(8) + Buf(8) + Len(8) = 24 字节
LINE_SIZE: t.CDefine = 24
def new_line(pool: memhub.MemBuddy | t.CPtr, text: t.CChar | t.CPtr) -> Line | t.CPtr:
"""创建一个指令行,复制 text 到新分配的缓冲区"""
ptr: Line | t.CPtr = pool.alloc(LINE_SIZE)
if ptr is None: return None
string.memset(ptr, 0, LINE_SIZE)
if text is not None:
slen: t.CSizeT = string.strlen(text)
buf: t.CChar | t.CPtr = pool.alloc(slen + 1)
if buf is not None:
string.strcpy(buf, text)
ptr.Buf = buf
ptr.Len = slen
return ptr
def _new_gslist(pool: memhub.MemBuddy | t.CPtr, list_size: t.CSizeT) -> t.CPtr:
"""从 pool 分配并零初始化一个 GSList 容器(Head/Tail/Count 全零)"""
gsl: t.CPtr = pool.alloc(list_size)
if gsl is None: return None
string.memset(gsl, 0, list_size)
return gsl
# BasicBlock 硬编码大小: Next(8) + Name(8) + Lines(8) + IsTerminated(4) = 32 字节
BLOCK_SIZE: t.CDefine = 32
def new_basic_block(pool: memhub.MemBuddy | t.CPtr, name: t.CChar | t.CPtr) -> BasicBlock | t.CPtr:
"""创建一个基本块"""
ptr: BasicBlock | t.CPtr = pool.alloc(BLOCK_SIZE)
if ptr is None: return None
string.memset(ptr, 0, BLOCK_SIZE)
ptr.Name = name
ptr.Lines = _new_gslist(pool, 24) # GSList: Head+Tail+Count
ptr.IsTerminated = 0
return ptr
# Param 硬编码大小: Next(8) + Ty(8) + Name(8) + Attrs(8) = 32 字节
PARAM_SIZE: t.CDefine = 32
def new_param(pool: memhub.MemBuddy | t.CPtr, ty: LLVMType | t.CPtr,
name: t.CChar | t.CPtr) -> Param | t.CPtr:
"""创建一个函数参数"""
ptr: Param | t.CPtr = pool.alloc(PARAM_SIZE)
if ptr is None: return None
string.memset(ptr, 0, PARAM_SIZE)
ptr.Ty = ty
ptr.Name = name
ptr.Attrs = None
return ptr
# Function 硬编码大小: Next(8) + Name(8) + RetTy(8) + Params(8) + Blocks(8) + IsDeclared(4) + IsVarArg(4) + Attrs(8) = 56 字节
FUNC_SIZE: t.CDefine = 56
def new_function(pool: memhub.MemBuddy | t.CPtr, name: t.CChar | t.CPtr,
ret_ty: LLVMType | t.CPtr) -> Function | t.CPtr:
"""创建一个函数"""
ptr: Function | t.CPtr = pool.alloc(FUNC_SIZE)
if ptr is None: return None
string.memset(ptr, 0, FUNC_SIZE)
ptr.Name = name
ptr.RetTy = ret_ty
ptr.Params = _new_gslist(pool, 24) # GSList: Head+Tail+Count
ptr.Blocks = _new_gslist(pool, 24)
ptr.IsDeclared = 0
ptr.Attrs = None
return ptr
# ============================================================
# 链表操作(委托 GSList.append)
# ============================================================
def function_add_param(func: Function | t.CPtr, param: Param | t.CPtr):
"""将参数追加到函数参数链表"""
if func is None or param is None: return
if func.Params is None: return
func.Params.append(param)
def function_add_block(func: Function | t.CPtr, block: BasicBlock | t.CPtr):
"""将基本块追加到函数块链表"""
if func is None or block is None: return
if func.Blocks is None: return
func.Blocks.append(block)
def function_move_block_to_end(func: Function | t.CPtr, block: BasicBlock | t.CPtr):
"""将 block 移动到函数块链表末尾(保证 BB 文本顺序与控制流顺序一致)。
用于解决 SSA 名非单调问题:BB 按创建顺序排列,但指令按控制流顺序生成,
导致 SSA 编号在文本输出时非单调递增,llc 报错。
在 position_at_end 时调用此函数,使 BB 顺序与 position 顺序一致。
注意:必须使用 GSList[BasicBlock] 带类型参数,否则 TransPyC 创建不完整
struct 类型,导致 blocks.Head/Tail/Count 字段访问读取垃圾值。
"""
if func is None or block is None: return
blocks: GSList[BasicBlock] | t.CPtr = func.Blocks
if blocks is None: return
# 已经是 Tail,无需移动
if blocks.Tail is block:
return
# 从链表中移除 block
if blocks.Head is block:
# block 是 Head,直接后移 Head
blocks.Head = block.Next
else:
# 遍历找前驱
prev: BasicBlock | t.CPtr = blocks.Head
while prev is not None and prev.Next is not block:
prev = prev.Next
if prev is None:
# block 不在链表中,直接 append
blocks.append(block)
return
prev.Next = block.Next
# block.Next 由移除逻辑处理,重置为 None
block.Next = None
# append 到末尾(不调用 blocks.append 以避免 Count 重复递增)
if blocks.Tail is not None:
blocks.Tail.Next = block
blocks.Tail = block
def block_append_line(block: BasicBlock | t.CPtr, line: Line | t.CPtr):
"""将指令行追加到基本块"""
if block is None or line is None: return
if block.Lines is None: return
block.Lines.append(line)
def block_set_terminated(block: BasicBlock | t.CPtr, val: t.CInt):
"""设置块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)"""
if block is None: return
block.IsTerminated = val
def block_get_terminated(block: BasicBlock | t.CPtr) -> t.CInt:
"""获取块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)"""
if block is None:
return 1
return block.IsTerminated
def block_append_text(pool: memhub.MemBuddy | t.CPtr, block: BasicBlock | t.CPtr,
text: t.CChar | t.CPtr):
"""创建指令行并追加到基本块"""
if block is None or text is None: return
line: Line | t.CPtr = new_line(pool, text)
if line is not None:
block_append_line(block, line)
def block_insert_text_before_terminator(pool: memhub.MemBuddy | t.CPtr,
block: BasicBlock | t.CPtr,
text: t.CChar | t.CPtr):
"""创建指令行并插入到基本块终止指令之前
如果块未终止,追加到末尾。
如果块已终止(有 br/ret),在终止指令之前插入,确保 alloca 等指令
不出现在终止指令之后。
"""
if block is None or text is None: return
if block.Lines is None: return
line: Line | t.CPtr = new_line(pool, text)
if line is None: return
# 块未终止:直接追加
if block.IsTerminated == 0:
block_append_line(block, line)
return
# 块已终止:在 Tail(终止指令)之前插入
lines: GSList[Line] | t.CPtr = block.Lines
if lines.Head is None:
block_append_line(block, line)
return
# 只有一个节点:在 Head 之前插入
if lines.Head is lines.Tail:
line.Next = lines.Head
lines.Head = line
lines.Count += 1
return
# 找到 Tail 的前驱节点
prev: Line | t.CPtr = lines.Head
while prev is not None and prev.Next is not lines.Tail:
prev = prev.Next
if prev is None:
# 找不到前驱,回退到追加
block_append_line(block, line)
return
# 在 prev 和 Tail 之间插入 line
line.Next = lines.Tail
prev.Next = line
lines.Count += 1
# ============================================================
# 属性设置
# ============================================================
def function_set_attrs(func: Function | t.CPtr, attrs: t.CChar | t.CPtr):
"""设置函数级属性文本(如 "nounwind"/"noinline")"""
if func is None: return
func.Attrs = attrs
def param_set_attrs(param: Param | t.CPtr, attrs: t.CChar | t.CPtr):
"""设置参数属性文本(如 "noundef"/"nocapture"/"readonly")"""
if param is None: return
param.Attrs = attrs
# ============================================================
# _ll_name_needs_quote - 检查 LLVM 标识符是否需要加引号
#
# LLVM IR 规范:标识符合法字符集为 [-a-zA-Z$._][-a-zA-Z$._0-9]*
# 以数字开头或含非合法字符(如 '['、']'、' '、'@' 等)时必须用双引号包围。
# '.' 是合法字符,不需要加引号(如 @.str、@llvm.memcpy 都不加引号)。
# SHA1 命名空间的函数名(如 "4dad2ba72ed7ba09.GSListNode[Param]")含 '['、']',
# 需要加引号;但纯 hex+点 的名字(如 "abc.def")不需要。
# ============================================================
def _ll_name_needs_quote(name: str) -> t.CInt:
"""检查 LLVM 标识符是否需要加引号(以数字开头或含非合法字符)
LLVM IR 合法字符:a-z, A-Z, 0-9, -, $, ., _
首字符是数字时需要加引号(LLVM 要求局部/全局标识符首字符非数字,除非加引号)。
含其他字符(如 '['、']'、空格)时需要加引号。
"""
if name is None:
return 0
c0: t.CChar = name[0]
if c0 >= '0' and c0 <= '9':
return 1
name_len: t.CSizeT = string.strlen(name)
i: t.CSizeT = 0
while i < name_len:
ch: t.CChar = name[i]
# 合法字符:a-z, A-Z, 0-9, -, $, ., _
if not ((ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z') or
(ch >= '0' and ch <= '9') or ch == '-' or ch == '$' or
ch == '.' or ch == '_'):
return 1
i += 1
return 0
# ============================================================
# RenumberSSA: 重编号函数内数字 SSA 名
#
# 解决 build_alloca_at_entry 在入口块终止指令前插入 alloca 导致
# SSA 名在文本输出中非单调递增的问题(llc 报错:
# "instruction expected to be numbered '%N' or greater")。
#
# 现象: entry 块原指令 %0-%6 + br,alloca_at_entry 插入 %28/%41 到 br 之前,
# 文本输出顺序为 entry(%0..%6, %28, %41) → if.then(%7..%27),
# 导致 %28 出现在 %7 之前,llc 拒绝编译。
#
# 算法:
# 1. 扫描所有 BB 指令行,按文本输出顺序收集数字 SSA 名 %N
# 2. 按首次出现顺序建立 old→new 映射(从 0 递增)
# 3. 替换所有指令行文本中的 %N 为新编号
#
# 注意:
# - 仅重编号纯数字 SSA 名(%0, %1, ...)
# - 命名 SSA 名(%entry, %.tmp, %self)、块标签、全局符号 @N 不重编号
# - 仅对 define(有函数体)调用,declare 无函数体无需处理
# - 当前 TransPyV 参数多为命名(%self/%env/%n),非数字,无需重编号参数
# ============================================================
# 判定字符是否为十进制数字
def _is_digit_ch(ch: t.CChar) -> t.CInt:
"""判断字符是否是 '0'-'9'
ch 是值类型 (t.CChar = i8),TPC 已修复值类型 is None 处理:
值类型 is None 恒为 false(生成 i1 0 常量),不会解引用空指针。
"""
if ch is None:
return 0
if ch >= '0' and ch <= '9':
return 1
return 0
# ============================================================
# i32 数组的安全访问(显式字节偏移)
#
# TransPyC 的 ptr[i] 指针索引可能被当作字节索引(i8*)而非 i32* 索引,
# 导致内存损坏。必须显式计算字节偏移:addr = base + idx * 4,再转型为 i32*。
# 参见 HandlesTranslator.py:358 的 t.CUInt64T(ptr) + offset * sizeof 用法。
# ============================================================
def _map_get_i32(map_arr: t.CChar | t.CPtr, idx: t.CInt) -> t.CInt:
"""读取 i32 数组 map_arr[idx](显式字节偏移 idx*4)
使用 (t.CInt | t.CPtr)(uint64) 强转将字节偏移转为 i32*,再用 c.Deref 解引用。
TPC 已修复类型联合强转(_HandleTypeUnionCastLlvm 添加 fallback),此写法可用。
"""
if map_arr is None:
return -1
byte_off: t.CUInt64T = t.CUInt64T(map_arr) + idx * 4
elem_ptr: t.CInt | t.CPtr = (t.CInt | t.CPtr)(byte_off)
return c.Deref(elem_ptr)
def _map_set_i32(map_arr: t.CChar | t.CPtr, idx: t.CInt, val: t.CInt):
"""写入 map_arr[idx] = val(i32 数组,显式字节偏移 idx*4)
使用 (t.CInt | t.CPtr)(uint64) 强转 + c.DerefAs 解引用赋值。
TPC 已修复类型联合强转,此写法可用。
"""
if map_arr is None:
return
byte_off: t.CUInt64T = t.CUInt64T(map_arr) + idx * 4
elem_ptr: t.CInt | t.CPtr = (t.CInt | t.CPtr)(byte_off)
c.DerefAs(elem_ptr, val)
def _scan_max_ssa_in_text(text: t.CChar | t.CPtr, cur_max: t.CInt) -> t.CInt:
"""扫描文本,找出 %N(N 为数字)中最大的 N,与 cur_max 比较返回更大值
仅匹配 '%' 后紧跟数字的情况,跳过命名 SSA 名(%entry 等)。
"""
result: t.CInt = cur_max
p: t.CChar | t.CPtr = text
while p is not None and p[0] != '\0':
if p[0] == '%':
nxt: t.CChar | t.CPtr = p + 1
if nxt is not None and _is_digit_ch(nxt[0]) != 0:
num: t.CInt = string.atoi(nxt)
if num > result:
result = num
p = p + 1
return result
def _find_max_ssa_number(func: Function | t.CPtr) -> t.CInt:
"""遍历所有 BB 指令行,找出最大的 %N 数字;无数字 SSA 名返回 -1"""
max_n: t.CInt = -1
if func is None or func.Blocks is None:
return max_n
blk: BasicBlock | t.CPtr = func.Blocks.Head
while blk is not None:
if blk.Lines is not None:
line: Line | t.CPtr = blk.Lines.Head
while line is not None:
if line.Buf is not None:
max_n = _scan_max_ssa_in_text(line.Buf, max_n)
line = line.Next
blk = blk.Next
return max_n
def _assign_new_numbers(text: t.CChar | t.CPtr,
map_arr: t.CChar | t.CPtr,
max_n: t.CInt, next_new: t.CInt) -> t.CInt:
"""扫描文本,对每个 %N(数字),若 map_arr[N]==-1 则分配 next_new
返回更新后的 next_new。按文本首次出现顺序建立映射。
"""
result: t.CInt = next_new
p: t.CChar | t.CPtr = text
while p is not None and p[0] != '\0':
if p[0] == '%':
nxt: t.CChar | t.CPtr = p + 1
if nxt is not None and _is_digit_ch(nxt[0]) != 0:
num: t.CInt = string.atoi(nxt)
if num >= 0 and num <= max_n:
if _map_get_i32(map_arr, num) == -1:
_map_set_i32(map_arr, num, result)
result += 1
p = p + 1
return result
def _replace_ssa_numbers(text: t.CChar | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
map_arr: t.CChar | t.CPtr,
max_n: t.CInt) -> t.CChar | t.CPtr:
"""构建新文本,将 %N 替换为映射后的新编号
逐字符扫描:遇到 %N(数字)查映射表替换,其他字符原样复制。
返回新分配的缓冲区(调用方无需释放旧 Buf,pool 统一管理)。
"""
src_len: t.CSizeT = string.strlen(text)
# 新缓冲区:最坏情况每个数字变长(位数增加),分配 src_len*2 + 64
new_size: t.CSizeT = src_len * 2 + 64
new_buf: t.CChar | t.CPtr = pool.alloc(new_size)
if new_buf is None:
return None
# 一次性分配数字缓冲区(循环中复用)
num_buf: t.CChar | t.CPtr = pool.alloc(16)
if num_buf is None:
new_buf[0] = '\0'
return new_buf
dst_idx: t.CSizeT = 0
src_p: t.CChar | t.CPtr = text
while src_p is not None and src_p[0] != '\0':
if dst_idx + 16 >= new_size:
# 缓冲区不足,截断
break
if src_p[0] == '%':
nxt: t.CChar | t.CPtr = src_p + 1
if nxt is not None and _is_digit_ch(nxt[0]) != 0:
num: t.CInt = string.atoi(nxt)
# 先做范围检查再读 map_arr:num 超 max_n 时 map_arr 只有
# (max_n+1)*4 字节,先读后查会越界读远端内存
if num >= 0 and num <= max_n:
new_num: t.CInt = _map_get_i32(map_arr, num)
if new_num != -1:
# 写入 '%'
new_buf[dst_idx] = '%'
dst_idx += 1
# 写入新编号
num_buf[0] = '\0'
viperlib.snprintf(num_buf, 16, "%d", new_num)
k: t.CSizeT = 0
while num_buf[k] != '\0' and dst_idx + 1 < new_size:
new_buf[dst_idx] = num_buf[k]
dst_idx += 1
k += 1
# 跳过旧数字字符
src_p = src_p + 1
while src_p is not None and _is_digit_ch(src_p[0]) != 0:
src_p = src_p + 1
continue
# 普通字符:直接复制
new_buf[dst_idx] = src_p[0]
dst_idx += 1
src_p = src_p + 1
new_buf[dst_idx] = '\0'
return new_buf
def RenumberSSA(func: Function | t.CPtr, pool: memhub.MemBuddy | t.CPtr):
"""重编号函数内所有数字 SSA 名,确保按文本输出顺序单调递增
在 FunctionPrint 序列化函数体之前调用。
仅对 define(IsDeclared==0)且 Blocks 非空 的函数生效。
"""
if func is None or pool is None:
return
if func.IsDeclared == 1:
return # declare 无函数体
if func.Blocks is None:
return
# 1. 第一遍扫描:找出最大 SSA 编号
max_n: t.CInt = _find_max_ssa_number(func)
if max_n < 0:
return # 无数字 SSA 名
# 2. 分配并初始化映射数组 map_arr[0..max_n] = -1
# 用 i8* 存储(pool.alloc 返回 i8*),通过 _map_get_i32/_map_set_i32 访问。
# 初始化为 -1:每个 i32 元素 4 字节,0xFFFFFFFF = -1(有符号 i32)。
# 用 memset 一次性填充,避免循环内 ptr[i] 指针索引问题。
map_bytes: t.CSizeT = (max_n + 1) * 4 # t.CInt = 4 字节
map_arr: t.CChar | t.CPtr = pool.alloc(map_bytes)
if map_arr is None:
return
string.memset(map_arr, 0xFF, map_bytes) # 0xFF * 4 = 0xFFFFFFFF = -1
# 3. 第二遍扫描:按文本顺序分配新编号
next_new: t.CInt = 0
blk: BasicBlock | t.CPtr = func.Blocks.Head
while blk is not None:
if blk.Lines is not None:
line: Line | t.CPtr = blk.Lines.Head
while line is not None:
if line.Buf is not None:
next_new = _assign_new_numbers(line.Buf, map_arr, max_n, next_new)
line = line.Next
blk = blk.Next
# 4. 检查是否有需要重编号的(old != new)
need_renumber: t.CInt = 0
i: t.CInt = 0
while i <= max_n:
v: t.CInt = _map_get_i32(map_arr, i)
if v != -1 and v != i:
need_renumber = 1
break
i += 1
if need_renumber == 0:
return # 编号已单调,无需替换
# 5. 第三遍扫描:替换所有指令行文本
blk = func.Blocks.Head
while blk is not None:
if blk.Lines is not None:
line: Line | t.CPtr = blk.Lines.Head
while line is not None:
if line.Buf is not None:
new_buf: t.CChar | t.CPtr = _replace_ssa_numbers(
line.Buf, pool, map_arr, max_n)
if new_buf is not None:
line.Buf = new_buf
line.Len = string.strlen(new_buf)
line = line.Next
blk = blk.Next
# ============================================================
# FunctionPrint: 将函数序列化为 IR 文本
#
# 格式:
# define <ret_ty> @<name>(<params>) {
# <block_name>:
# <instruction>
# <instruction>
# <block_name>:
# <instruction>
# }
# ============================================================
def FunctionPrint(buf: t.CChar | t.CPtr, size: t.CSizeT,
func: Function | t.CPtr, pool: memhub.MemBuddy | t.CPtr):
"""将函数 func 序列化为 IR 文本写入 buf"""
if func is None or buf is None or size == 0: return
# 在序列化前重编号数字 SSA 名,确保按文本输出顺序单调递增。
# 解决 build_alloca_at_entry 在入口块终止指令前插入 alloca 导致
# SSA 名非单调(llc 报错 "instruction expected to be numbered '%N' or greater")。
# RenumberSSA 内部会跳过 declare 函数(IsDeclared==1),无需在此判断。
RenumberSSA(func, pool)
ty_buf: t.CChar | t.CPtr = pool.alloc(128)
if ty_buf is None: return
ty_buf[0] = '\0'
# define <ret_ty> @<name>(<params>)
if func.IsDeclared == 1:
_append_cstr(buf, size, "declare ")
else:
_append_cstr(buf, size, "define ")
ty_buf[0] = '\0'
TypePrint(ty_buf, 128, func.RetTy, pool)
_append_cstr(buf, size, ty_buf)
_append_cstr(buf, size, " @")
if func.Name is not None:
if _ll_name_needs_quote(func.Name) != 0:
_append_cstr(buf, size, "\"")
_append_cstr(buf, size, func.Name)
_append_cstr(buf, size, "\"")
else:
_append_cstr(buf, size, func.Name)
_append_cstr(buf, size, "(")
# 参数列表(从 GSList[Param].Head 遍历)
cur: Param | t.CPtr = None
if func.Params is not None:
cur = func.Params.Head
first: t.CInt = 1
while cur is not None:
if first == 0:
_append_cstr(buf, size, ", ")
# 参数属性(如 "noundef"/"nocapture")在类型之前
if cur.Attrs is not None:
_append_cstr(buf, size, cur.Attrs)
_append_cstr(buf, size, " ")
ty_buf[0] = '\0'
TypePrint(ty_buf, 128, cur.Ty, pool)
_append_cstr(buf, size, ty_buf)
# declare 和 define 都打印参数名(如果有)
if cur.Name is not None:
_append_cstr(buf, size, " ")
# LLVM IR 要求参数名以 % 开头
if cur.Name[0] != '%':
_append_cstr(buf, size, "%")
_append_cstr(buf, size, cur.Name)
cur = cur.Next
first = 0
# 变参函数追加 ...
if func.IsVarArg == 1:
if func.Params is not None and func.Params.Head is not None:
_append_cstr(buf, size, ", ...")
else:
_append_cstr(buf, size, "...")
_append_cstr(buf, size, ")")
# 函数级属性(如 "nounwind"/"noinline")在 ) 之后
if func.Attrs is not None:
_append_cstr(buf, size, " ")
_append_cstr(buf, size, func.Attrs)
if func.IsDeclared == 1:
_append_cstr(buf, size, "\n")
return
# 函数体
_append_cstr(buf, size, " {\n")
blk: BasicBlock | t.CPtr = None
if func.Blocks is not None:
blk = func.Blocks.Head
while blk is not None:
# 块标签
if blk.Name is not None:
_append_cstr(buf, size, blk.Name)
_append_cstr(buf, size, ":\n")
# 指令行(从 GSList[Line].Head 遍历)
line: Line | t.CPtr = None
if blk.Lines is not None:
line = blk.Lines.Head
has_lines: t.CInt = 0
while line is not None:
if line.Buf is not None:
_append_cstr(buf, size, " ")
_append_cstr(buf, size, line.Buf)
_append_cstr(buf, size, "\n")
has_lines = 1
line = line.Next
# 块未终止兜底:LLVM IR 要求每个基本块以终止指令(br/ret/unreachable)结尾。
# 两种情况需要补 unreachable:
# 1. 空块(has_lines==0):标签后无任何指令
# 2. 有指令但未终止(IsTerminated==0):如 break/continue 后的 dead.N 块
# 被后续语句填入指令但缺少终止符,导致下一个标签处报
# "expected instruction opcode"
if has_lines == 0 or blk.IsTerminated == 0:
_append_cstr(buf, size, " unreachable\n")
blk = blk.Next
_append_cstr(buf, size, "}\n")
# ============================================================
# 内部辅助
# ============================================================
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)
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
if dlen >= dst_size:
return
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'
# ============================================================
# 字段访问器(供其他模块绕过 stub 类型限制使用)
#
# 跨模块编译时,使用方模块只有 Function/Param 的 stub 类型
# (字段不足且字段类型可能错误),导致 TransPyC 静默跳过
# 字段访问(不生成 GEP,不报错),字段值永远为 null。
#
# 这些访问器在拥有完整类型定义的本模块内执行,能正确访问
# 所有字段。其他模块通过函数调用获取字段值,绕过 stub 限制。
# ============================================================
def function_get_name(func: Function | t.CPtr) -> t.CChar | t.CPtr:
"""获取函数名"""
if func is None:
return None
return func.Name
def function_get_ret_ty(func: Function | t.CPtr) -> LLVMType | t.CPtr:
"""获取函数返回类型"""
if func is None:
return None
return func.RetTy
def function_get_params(func: Function | t.CPtr) -> GSList[Param] | t.CPtr:
"""获取函数参数链表容器"""
if func is None:
return None
return func.Params
def function_get_param_head(func: Function | t.CPtr) -> Param | t.CPtr:
"""获取函数参数链表头节点(一步到位,避免调用方访问 GSList.Head)"""
if func is None:
return None
if func.Params is None:
return None
return func.Params.Head
def function_get_next(func: Function | t.CPtr) -> Function | t.CPtr:
"""获取链表下一个函数"""
if func is None:
return None
return func.Next
def function_is_declared(func: Function | t.CPtr) -> t.CInt:
"""获取函数是否为声明(1=declare 仅声明, 0=define 定义)"""
if func is None:
return 0
return func.IsDeclared
def param_get_name(param: Param | t.CPtr) -> t.CChar | t.CPtr:
"""获取参数名"""
if param is None:
return None
return param.Name
def param_get_ty(param: Param | t.CPtr) -> LLVMType | t.CPtr:
"""获取参数类型"""
if param is None:
return None
return param.Ty
def param_get_next(param: Param | t.CPtr) -> Param | t.CPtr:
"""获取链表下一个参数"""
if param is None:
return None
return param.Next