修正了一些错误
This commit is contained in:
@@ -227,6 +227,48 @@ def block_append_text(pool: memhub.MemBuddy | t.CPtr, block: BasicBlock | t.CPtr
|
||||
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
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 属性设置
|
||||
# ============================================================
|
||||
@@ -276,6 +318,259 @@ def _ll_name_needs_quote(name: str) -> t.CInt:
|
||||
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 文本
|
||||
#
|
||||
@@ -293,6 +588,12 @@ def FunctionPrint(buf: t.CChar | t.CPtr, size: t.CSizeT,
|
||||
"""将函数 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'
|
||||
@@ -371,12 +672,22 @@ def FunctionPrint(buf: t.CChar | t.CPtr, size: t.CSizeT,
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user