786 lines
29 KiB
Python
786 lines
29 KiB
Python
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_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)
|
||
new_num: t.CInt = _map_get_i32(map_arr, num)
|
||
if num >= 0 and num <= max_n and 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)
|
||
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
|