1421 lines
62 KiB
Plaintext
1421 lines
62 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import string
|
||
import viperlib
|
||
import memhub
|
||
import stdio
|
||
import stdlib
|
||
from linkedlist import GSListNode, GSList
|
||
from .__types import LLVMType, ParamNode, TypePrint
|
||
|
||
|
||
# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast,与 __module.vp 对齐)。
|
||
# 函数/基本块/指令行链的 Next 被越界写破坏成环时,遍历无限循环挂起;
|
||
# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位"。
|
||
IR_DUMP_CHAIN_LIMIT: t.CDefine = 100000
|
||
|
||
# 窥孔优化总开关(T2 转发/ret 归零、T3 no-op bitcast 消除)。
|
||
# TPV 命令行 --no-llvmpeephole 会将其置 0:FunctionPrint 序列化时跳过全部本地窥孔,
|
||
# 便于与关闭窥孔的 IR 对比调试(RenumberSSA 仍保留,保证输出 SSA 编号合法)。
|
||
G_PeepEnable: bool = True
|
||
|
||
|
||
# ============================================================
|
||
# 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: str # 行文本(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: str # 块名(如 "entry"/"if.then")
|
||
Lines: rawptr[GSList[Line]] # 指令行链表容器(Head/Tail/Count)
|
||
IsTerminated: int # 1=已有终止指令(br/ret)
|
||
|
||
|
||
# ============================================================
|
||
# Param: 函数参数
|
||
#
|
||
# 继承 GSListNode[Param] 获取强类型 Next: Param|CPtr
|
||
# Attrs 存储参数属性文本(如 "noundef"/"nocapture"/"readonly"),None=无属性
|
||
#
|
||
# 注意: 必须加 @t.NoVTable(理由同 Line)。
|
||
# ============================================================
|
||
@t.NoVTable
|
||
class Param(GSListNode[Param]):
|
||
Ty: rawptr[LLVMType] # 参数类型
|
||
Name: str # 参数名(如 "%0"/"%a")
|
||
Attrs: str # 参数属性文本(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: str # 函数名
|
||
RetTy: rawptr[LLVMType] # 返回类型
|
||
Params: rawptr[GSList[Param]] # 参数链表容器
|
||
Blocks: rawptr[GSList[BasicBlock]] # 基本块链表容器
|
||
IsDeclared: int # 1=仅声明(declare), 0=定义(define)
|
||
IsVarArg: int # 1=变参函数(...)
|
||
Attrs: str # 函数属性文本(None=无属性)
|
||
AttrGroup: str # 字符串属性组内容(如 '"section"=".text.startup"',None=无)
|
||
AttrGroupId: int # 属性组编号(>0 时函数行输出 #N,模块尾输出 attributes #N)
|
||
NameLen: t.CSizeT # 函数名长度(创建时一次算好,恒等于 strlen(Name);追加在末尾,既有字段偏移不变)
|
||
|
||
|
||
# ============================================================
|
||
# 工厂函数
|
||
# ============================================================
|
||
# Line 硬编码大小: Next(8) + Buf(8) + Len(8) = 24 字节
|
||
LINE_SIZE: t.CDefine = 24
|
||
|
||
def new_line(pool: rawptr[memhub.MemBuddy], text: str) -> rawptr[Line]:
|
||
"""创建一个指令行,复制 text 到新分配的缓冲区"""
|
||
ptr: rawptr[Line] = 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: str = 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: rawptr[memhub.MemBuddy], 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: rawptr[memhub.MemBuddy], name: str) -> rawptr[BasicBlock]:
|
||
"""创建一个基本块"""
|
||
ptr: rawptr[BasicBlock] = 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: rawptr[memhub.MemBuddy], ty: rawptr[LLVMType],
|
||
name: str) -> rawptr[Param]:
|
||
"""创建一个函数参数"""
|
||
ptr: rawptr[Param] = 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) + AttrGroup(8) + AttrGroupId(8) + NameLen(8) = 80 字节
|
||
FUNC_SIZE: t.CDefine = 80
|
||
|
||
def new_function(pool: rawptr[memhub.MemBuddy], name: str,
|
||
ret_ty: rawptr[LLVMType]) -> rawptr[Function]:
|
||
"""创建一个函数"""
|
||
ptr: rawptr[Function] = 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
|
||
ptr.AttrGroup = None
|
||
ptr.AttrGroupId = 0
|
||
# NameLen 创建时一次算好:Name 终生不变(本文件仅此处赋值 ptr.Name),
|
||
# 故 NameLen 恒等于 strlen(Name),可替代对函数名的反复 strlen 热点。
|
||
# name=None 时置 0,与 new_line 的 "text is not None" 判空约定一致
|
||
# (memset 已归零,此处显式赋值以固定语义、不依赖分配器行为)。
|
||
if name is not None:
|
||
ptr.NameLen = string.strlen(name)
|
||
else:
|
||
ptr.NameLen = 0
|
||
return ptr
|
||
|
||
|
||
# ============================================================
|
||
# 链表操作(委托 GSList.append)
|
||
# ============================================================
|
||
def function_add_param(func: rawptr[Function], param: rawptr[Param]):
|
||
"""将参数追加到函数参数链表"""
|
||
if func is None or param is None: return
|
||
# 注意:必须先用局部变量 + 类型参数(对齐 function_move_block_to_end 注释),
|
||
# 链式 func.Params.append() 会被编译器解析成 LinkedNode.append 且 this 前移
|
||
# sizeof(LinkedNode)(兄弟 Mixin 兜底误匹配),写穿容器前 48 字节破坏数据结构。
|
||
params: rawptr[GSList[Param]] = func.Params
|
||
if params is None: return
|
||
params.append(param)
|
||
|
||
|
||
def function_add_block(func: rawptr[Function], block: rawptr[BasicBlock]):
|
||
"""将基本块追加到函数块链表"""
|
||
if func is None or block is None: return
|
||
blocks: rawptr[GSList[BasicBlock]] = func.Blocks
|
||
if blocks is None: return
|
||
blocks.append(block)
|
||
|
||
|
||
def function_move_block_to_end(func: rawptr[Function], block: rawptr[BasicBlock]):
|
||
"""将 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: rawptr[GSList[BasicBlock]] = 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: rawptr[BasicBlock] = 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: rawptr[BasicBlock], line: rawptr[Line]):
|
||
"""将指令行追加到基本块"""
|
||
if block is None or line is None: return
|
||
lines: rawptr[GSList[Line]] = block.Lines
|
||
if lines is None: return
|
||
lines.append(line)
|
||
|
||
|
||
def block_set_terminated(block: rawptr[BasicBlock], val: int):
|
||
"""设置块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)"""
|
||
if block is None: return
|
||
block.IsTerminated = val
|
||
|
||
|
||
def block_get_terminated(block: rawptr[BasicBlock]) -> int:
|
||
"""获取块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)"""
|
||
if block is None:
|
||
return 1
|
||
return block.IsTerminated
|
||
|
||
|
||
def block_append_text(pool: rawptr[memhub.MemBuddy], block: rawptr[BasicBlock],
|
||
text: str):
|
||
"""创建指令行并追加到基本块"""
|
||
if block is None or text is None: return
|
||
line: rawptr[Line] = new_line(pool, text)
|
||
if line is not None:
|
||
block_append_line(block, line)
|
||
|
||
|
||
def block_insert_text_before_terminator(pool: rawptr[memhub.MemBuddy],
|
||
block: rawptr[BasicBlock],
|
||
text: str):
|
||
"""创建指令行并插入到基本块终止指令之前
|
||
|
||
如果块未终止,追加到末尾。
|
||
如果块已终止(有 br/ret),在终止指令之前插入,确保 alloca 等指令
|
||
不出现在终止指令之后。
|
||
"""
|
||
if block is None or text is None: return
|
||
if block.Lines is None: return
|
||
line: rawptr[Line] = new_line(pool, text)
|
||
if line is None: return
|
||
# 块未终止:直接追加
|
||
if block.IsTerminated == 0:
|
||
block_append_line(block, line)
|
||
return
|
||
# 块已终止:在 Tail(终止指令)之前插入
|
||
lines: rawptr[GSList[Line]] = 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: rawptr[Line] = 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: rawptr[Function], attrs: str):
|
||
"""设置函数级属性文本(如 "nounwind"/"noinline")"""
|
||
if func is None: return
|
||
func.Attrs = attrs
|
||
|
||
|
||
_g_attr_group_counter: int = 0
|
||
|
||
def function_set_attr_group(func: rawptr[Function], group: str):
|
||
"""设置函数字符串属性组(如 '"section"=".text.startup"')并分配组编号 #N。
|
||
|
||
LLVM 字符串属性(带值的,如 section)不能内联在函数签名行,
|
||
必须用属性组:函数行输出 #N,组定义输出在函数尾。
|
||
"""
|
||
global _g_attr_group_counter
|
||
if func is None or group is None:
|
||
return
|
||
func.AttrGroup = group
|
||
_g_attr_group_counter += 1
|
||
func.AttrGroupId = _g_attr_group_counter
|
||
|
||
|
||
def param_set_attrs(param: rawptr[Param], attrs: str):
|
||
"""设置参数属性文本(如 "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) -> int:
|
||
"""检查 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) -> int:
|
||
"""判断字符是否是 '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: str, idx: int) -> int:
|
||
"""读取 i32 数组 map_arr[idx](显式字节偏移 idx*4)
|
||
|
||
使用 (rawptr[int])(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: rawptr[int] = (rawptr[int])(byte_off)
|
||
return c.Deref(elem_ptr)
|
||
|
||
|
||
def _map_set_i32(map_arr: str, idx: int, val: int):
|
||
"""写入 map_arr[idx] = val(i32 数组,显式字节偏移 idx*4)
|
||
|
||
使用 (rawptr[int])(uint64) 强转 + c.DerefAs 解引用赋值。
|
||
TPC 已修复类型联合强转,此写法可用。
|
||
"""
|
||
if map_arr is None:
|
||
return
|
||
byte_off: t.CUInt64T = t.CUInt64T(map_arr) + idx * 4
|
||
elem_ptr: rawptr[int] = (rawptr[int])(byte_off)
|
||
c.DerefAs(elem_ptr, val)
|
||
|
||
|
||
def _scan_max_ssa_in_text(text: str, cur_max: int) -> int:
|
||
"""扫描文本,找出 %N(N 为数字)中最大的 N,与 cur_max 比较返回更大值
|
||
|
||
仅匹配 '%' 后紧跟数字的情况,跳过命名 SSA 名(%entry 等)。
|
||
"""
|
||
result: int = cur_max
|
||
p: str = text
|
||
while p is not None and p[0] != '\0':
|
||
if p[0] == '%':
|
||
nxt: str = p + 1
|
||
if nxt is not None and _is_digit_ch(nxt[0]) != 0:
|
||
num: int = string.atoi(nxt)
|
||
if num > result:
|
||
result = num
|
||
p = p + 1
|
||
return result
|
||
|
||
|
||
def _find_max_ssa_number(func: rawptr[Function]) -> int:
|
||
"""遍历所有 BB 指令行,找出最大的 %N 数字;无数字 SSA 名返回 -1"""
|
||
max_n: int = -1
|
||
if func is None or func.Blocks is None:
|
||
return max_n
|
||
blocks: rawptr[GSList[BasicBlock]] = func.Blocks
|
||
blk: rawptr[BasicBlock] = blocks.Head
|
||
blk_steps: t.CSizeT = 0
|
||
while blk is not None:
|
||
blk_steps += 1
|
||
if blk_steps > IR_DUMP_CHAIN_LIMIT:
|
||
stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 扫描终止)\n", func.Name)
|
||
stdlib.exit(1)
|
||
if blk.Lines is not None:
|
||
lines: rawptr[GSList[Line]] = blk.Lines
|
||
line: rawptr[Line] = lines.Head
|
||
line_steps: t.CSizeT = 0
|
||
while line is not None:
|
||
line_steps += 1
|
||
if line_steps > IR_DUMP_CHAIN_LIMIT:
|
||
stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 扫描终止)\n",
|
||
func.Name, blk.Name)
|
||
stdlib.exit(1)
|
||
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: str,
|
||
map_arr: str,
|
||
max_n: int, next_new: int) -> int:
|
||
"""扫描文本,对每个 %N(数字),若 map_arr[N]==-1 则分配 next_new
|
||
|
||
返回更新后的 next_new。按文本首次出现顺序建立映射。
|
||
"""
|
||
result: int = next_new
|
||
p: str = text
|
||
while p is not None and p[0] != '\0':
|
||
if p[0] == '%':
|
||
nxt: str = p + 1
|
||
if nxt is not None and _is_digit_ch(nxt[0]) != 0:
|
||
num: int = 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: str,
|
||
pool: rawptr[memhub.MemBuddy],
|
||
map_arr: str,
|
||
max_n: int) -> str:
|
||
"""构建新文本,将 %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: str = pool.alloc(new_size)
|
||
if new_buf is None:
|
||
return None
|
||
# 一次性分配数字缓冲区(循环中复用)
|
||
num_buf: str = pool.alloc(16)
|
||
if num_buf is None:
|
||
new_buf[0] = '\0'
|
||
return new_buf
|
||
|
||
dst_idx: t.CSizeT = 0
|
||
src_p: str = text
|
||
while src_p is not None and src_p[0] != '\0':
|
||
if dst_idx + 16 >= new_size:
|
||
# 缓冲区不足,截断
|
||
break
|
||
if src_p[0] == '%':
|
||
nxt: str = src_p + 1
|
||
if nxt is not None and _is_digit_ch(nxt[0]) != 0:
|
||
num: int = string.atoi(nxt)
|
||
# 先做范围检查再读 map_arr:num 超 max_n 时 map_arr 只有
|
||
# (max_n+1)*4 字节,先读后查会越界读远端内存
|
||
if num >= 0 and num <= max_n:
|
||
new_num: int = _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: rawptr[Function], pool: rawptr[memhub.MemBuddy]):
|
||
"""重编号函数内所有数字 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: int = _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 # int = 4 字节
|
||
map_arr: str = pool.alloc(map_bytes)
|
||
if map_arr is None:
|
||
return
|
||
string.memset(map_arr, 0xFF, map_bytes) # 0xFF * 4 = 0xFFFFFFFF = -1
|
||
|
||
# 3. 第二遍扫描:按文本顺序分配新编号
|
||
next_new: int = 0
|
||
blocks: rawptr[GSList[BasicBlock]] = func.Blocks
|
||
blk: rawptr[BasicBlock] = blocks.Head
|
||
blk_steps: t.CSizeT = 0
|
||
while blk is not None:
|
||
blk_steps += 1
|
||
if blk_steps > IR_DUMP_CHAIN_LIMIT:
|
||
stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 重编号终止)\n", func.Name)
|
||
stdlib.exit(1)
|
||
if blk.Lines is not None:
|
||
lines: rawptr[GSList[Line]] = blk.Lines
|
||
line: rawptr[Line] = lines.Head
|
||
line_steps: t.CSizeT = 0
|
||
while line is not None:
|
||
line_steps += 1
|
||
if line_steps > IR_DUMP_CHAIN_LIMIT:
|
||
stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 重编号终止)\n",
|
||
func.Name, blk.Name)
|
||
stdlib.exit(1)
|
||
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: int = 0
|
||
i: int = 0
|
||
while i <= max_n:
|
||
v: int = _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 = blocks.Head
|
||
blk_steps = 0
|
||
while blk is not None:
|
||
blk_steps += 1
|
||
if blk_steps > IR_DUMP_CHAIN_LIMIT:
|
||
stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 替换终止)\n", func.Name)
|
||
stdlib.exit(1)
|
||
if blk.Lines is not None:
|
||
lines: rawptr[GSList[Line]] = blk.Lines
|
||
line: rawptr[Line] = lines.Head
|
||
line_steps = 0
|
||
while line is not None:
|
||
line_steps += 1
|
||
if line_steps > IR_DUMP_CHAIN_LIMIT:
|
||
stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 替换终止)\n",
|
||
func.Name, blk.Name)
|
||
stdlib.exit(1)
|
||
if line.Buf is not None:
|
||
new_buf: str = _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
|
||
|
||
|
||
# ============================================================
|
||
# 本地窥孔优化(文本层,输出前执行,保持语义)
|
||
# T1: ret <iN> zeroinitializer → ret <iN> 0;ret <ptr*> zeroinitializer → null
|
||
# T2: 入口 alloca 槽:唯一一次 store 且值无副作用(形参/%SSA/整数/浮点字面量)
|
||
# 时,把 store→load 转发为直接使用该值,删除 alloca/store/load 冗余行
|
||
# ("虚拟寄存器优化",如 printf(i8* %msg) 前不再 alloca+store+load)。
|
||
# 安全性:仅当 alloca 与唯一 store 都在 entry、loads 任意、无其它取址/别名用途。
|
||
# ============================================================
|
||
def _peep_is_startwith(s: str, pfx: str) -> int:
|
||
if s is None or pfx is None:
|
||
return 0
|
||
pl: t.CSizeT = string.strlen(pfx)
|
||
if pl == 0:
|
||
return 1
|
||
if string.strlen(s) < pl:
|
||
return 0
|
||
return 1 if string.strncmp(s, pfx, pl) == 0 else 0
|
||
|
||
|
||
def _peep_replace_token(pool: rawptr[memhub.MemBuddy],
|
||
line_buf: str, tok: str, repl: str,
|
||
tlen: t.CSizeT, rlen: t.CSizeT) -> str:
|
||
"""将 line_buf 中独立记号 %<tok> 替换为 repl,返回新缓冲(无变化返回原指针)
|
||
|
||
tlen/rlen = strlen(tok)/strlen(repl):由调用方在行循环外预先求好(同一
|
||
ltok/safe_val 会作用于本函数处理的所有行),避免每行重复求长。
|
||
"""
|
||
if line_buf is None or tok is None or repl is None:
|
||
return line_buf
|
||
if tlen == 0:
|
||
return line_buf
|
||
# 预扫是否命中:不依赖 strlen,以 NUL 为界单趟扫描(绝大多数调用无命中即返回,
|
||
# 从而省去逐行 strlen)。逐输入等价:tok 无 NUL(tlen=strlen(tok)),故
|
||
# strncmp 判等必蕴含记号完整落在串内(i+tlen 处仍非 NUL);凡 i+tlen>ln 的
|
||
# 位置 strncmp 必先遇到 line_buf 的 NUL 而与 tok 的非 NUL 字节不等,故原
|
||
# 上界 i+tlen<=ln 从未排除任一可能命中,去掉后命中判定不变。
|
||
hit: int = 0
|
||
i: t.CSizeT = 0
|
||
while line_buf[i] != '\0':
|
||
if line_buf[i] == '%' and line_buf[i + 1] == tok[0] \
|
||
and string.strncmp(line_buf + i + 1, tok, tlen) == 0:
|
||
nxt: int = 0
|
||
if line_buf[i + 1 + tlen] != '\0':
|
||
cc: int = line_buf[i + 1 + tlen]
|
||
if (cc >= 48 and cc <= 57) or (cc >= 65 and cc <= 90) or (cc >= 97 and cc <= 122) or cc == 95:
|
||
nxt = 1
|
||
if nxt == 0:
|
||
hit = 1
|
||
break
|
||
i += 1
|
||
if hit == 0:
|
||
return line_buf
|
||
ln: t.CSizeT = string.strlen(line_buf)
|
||
# 缓冲大小上界:单行最多 ⌊ln/2⌋ 次替换,每次去掉 ≥2 字节(%+名)换入 rlen 字节;
|
||
# 最坏输出 ≤ ln + (ln/2)·rlen + NUL。ln + 1 + rlen·(ln+1) 恒覆盖该上界,
|
||
# 避免 K≥2 且替换串更长时把新缓冲写穿(随机内存破坏)。
|
||
out: str = pool.alloc(ln + 1 + rlen * (ln + 1))
|
||
if out is None:
|
||
return line_buf
|
||
op: t.CSizeT = 0
|
||
oq: t.CSizeT = 0
|
||
i = 0
|
||
while i < ln:
|
||
if line_buf[i] == '%' and i + 1 + tlen <= ln \
|
||
and line_buf[i + 1] == tok[0] \
|
||
and string.strncmp(line_buf + i + 1, tok, tlen) == 0:
|
||
nxt2: int = 0
|
||
if i + 1 + tlen < ln:
|
||
cc2: int = line_buf[i + 1 + tlen]
|
||
if (cc2 >= 48 and cc2 <= 57) or (cc2 >= 65 and cc2 <= 90) or (cc2 >= 97 and cc2 <= 122) or cc2 == 95:
|
||
nxt2 = 1
|
||
if nxt2 == 0:
|
||
j: t.CSizeT = 0
|
||
while j < rlen:
|
||
out[oq] = repl[j]
|
||
oq += 1
|
||
j += 1
|
||
i += 1 + tlen
|
||
continue
|
||
out[oq] = line_buf[i]
|
||
oq += 1
|
||
i += 1
|
||
out[oq] = '\0'
|
||
return out
|
||
|
||
|
||
def _peep_last_ref_eq(sb: str, a_name: str, nl: t.CSizeT) -> int:
|
||
"""行内最后一个 '%name' 记号是否等于 a_name(store/load 的指针操作数在行尾)
|
||
|
||
nl = strlen(a_name):由调用方在 alloca 槽全量行扫描外预先求好(a_name 在
|
||
同一次槽扫描中不变),避免每行调用重复 strlen。
|
||
"""
|
||
if sb is None or a_name is None:
|
||
return 0
|
||
# 单趟定位最后一个 '%'(原实现反复 strchr 逐个跳转,每个 '%' 一次函数调用)
|
||
r: str = None
|
||
probe: str = sb
|
||
while probe[0] != '\0':
|
||
if probe[0] == '%':
|
||
r = probe
|
||
probe += 1
|
||
if r is None:
|
||
return 0
|
||
# 先比首字符(廉价拒绝),首字符相同才进入 strncmp
|
||
if r[1] == a_name[0] and string.strncmp(r + 1, a_name, nl) == 0:
|
||
nxtc: int = r[1 + nl]
|
||
if nxtc == 0 or nxtc == 32 or nxtc == 44 or nxtc == 41:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _peep_alloc_name(eb: str, pool: rawptr[memhub.MemBuddy]) -> str:
|
||
"""从 '%name = alloca ...' 提取 name(不含 %),失败返回 None"""
|
||
if eb is None or eb[0] != '%':
|
||
return None
|
||
eqpos: str = string.strstr(eb, " = alloca ")
|
||
if eqpos is None:
|
||
return None
|
||
nlen: t.CSizeT = t.CSizeT(t.CUInt64T(eqpos) - t.CUInt64T(eb)) - 1
|
||
if nlen == 0:
|
||
return None
|
||
nm: str = pool.alloc(nlen + 1)
|
||
if nm is None:
|
||
return None
|
||
string.strncpy(nm, eb + 1, nlen)
|
||
nm[nlen] = '\0'
|
||
return nm
|
||
|
||
|
||
def _peep_is_alloc_of(eb: str, a_name: str, nl: t.CSizeT) -> int:
|
||
"""判断 eb 是否为 a_name 的 alloca 定义行,等价于对 _peep_alloc_name(eb) 与
|
||
a_name 做 strcmp 是否为 0(不再分配名字缓冲)。
|
||
|
||
先比名字(strncmp,名字不同立即拒绝),仅当名字逐字节一致时才做
|
||
" = alloca " 子串定位——把原实现「每一个 '%' 行都调一次 strstr」的
|
||
O(槽数×行数) 次 strstr 降为每槽仅命中行才调用。
|
||
nl = strlen(a_name);nlen==0(名字为空)在原实现返回 None,故此处同样拒绝。
|
||
等价性:_peep_alloc_name 取第一个 " = alloca " 之前、'%' 之后的字节为名字,
|
||
故名字等长且一致 ⟺ strncmp 相同且该子串恰起于 eb+1+nl。
|
||
"""
|
||
if eb is None or a_name is None or nl == 0 or eb[0] != '%':
|
||
return 0
|
||
# 首字符预筛:nl>=1,strncmp 判等必然蕴含首字符相等;首字符不同即可廉价拒绝,
|
||
# 避免绝大多数行进入 strncmp 调用。逐输入等价:本分支拒绝当且仅当原 strncmp
|
||
# 也必返回非 0(首字节不同)。eb[0]=='%' 非 NUL ⇒ eb[1] 读在串内(最多为 NUL)。
|
||
if eb[1] != a_name[0]:
|
||
return 0
|
||
if string.strncmp(eb + 1, a_name, nl) != 0:
|
||
return 0
|
||
eqpos: str = string.strstr(eb, " = alloca ")
|
||
if eqpos is not None and t.CUInt64T(eqpos) == t.CUInt64T(eb) + 1 + nl:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _peephole_opt(func: rawptr[Function], pool: rawptr[memhub.MemBuddy]):
|
||
"""文本层本地窥孔:T1 ret zeroinitializer;T2 入口槽 store→load 转发"""
|
||
if func is None or func.Blocks is None or pool is None:
|
||
return
|
||
# 注:TPC 对 "变量 = 指针.字段.成员" 两级自动解引用链会静默丢弃赋值
|
||
# (编译出的 IR 直接读未初始化栈变量 → 随机栈错误)。必须拆成单级:
|
||
# 先取字段到局部 rawptr,再判空后取 .Head(与 FunctionPrint 同一模式)。
|
||
func_blocks: rawptr[GSList[BasicBlock]] = func.Blocks
|
||
blk: rawptr[BasicBlock] = None
|
||
if func_blocks is not None:
|
||
blk = func_blocks.Head
|
||
|
||
# ---------- T1: ret <ty> zeroinitializer ----------
|
||
blk_t1 = blk
|
||
while blk_t1 is not None:
|
||
line1: rawptr[Line] = None
|
||
if blk_t1.Lines is not None:
|
||
t1_lines: rawptr[GSList[Line]] = blk_t1.Lines
|
||
line1 = t1_lines.Head
|
||
while line1 is not None:
|
||
if line1.Buf is not None:
|
||
zi: str = string.strstr(line1.Buf, " zeroinitializer")
|
||
if zi is not None and _peep_is_startwith(line1.Buf, "ret ") != 0:
|
||
ty_len: t.CSizeT = t.CSizeT(t.CUInt64T(zi) - t.CUInt64T(line1.Buf)) - 4
|
||
if ty_len > 0:
|
||
ty_buf: str = pool.alloc(ty_len + 1)
|
||
if ty_buf is not None:
|
||
string.strncpy(ty_buf, line1.Buf + 4, ty_len)
|
||
ty_buf[ty_len] = '\0'
|
||
lit: str = None
|
||
if ty_buf[0] == 'i':
|
||
lit = "0"
|
||
elif string.strchr(ty_buf, 42) is not None: # '*'
|
||
lit = "null"
|
||
if lit is not None:
|
||
nb: str = pool.alloc(ty_len + 16)
|
||
if nb is not None:
|
||
nb[0] = '\0'
|
||
string.strcat(nb, "ret ")
|
||
string.strcat(nb, ty_buf)
|
||
string.strcat(nb, " ")
|
||
string.strcat(nb, lit)
|
||
line1.Buf = nb
|
||
line1 = line1.Next
|
||
blk_t1 = blk_t1.Next
|
||
|
||
# ---------- T2: entry alloca 槽转发 ----------
|
||
entry_blk: rawptr[BasicBlock] = blk
|
||
if entry_blk is None:
|
||
return
|
||
entry_lines: rawptr[GSList[Line]] = entry_blk.Lines
|
||
if entry_lines is None:
|
||
return
|
||
e_line: rawptr[Line] = entry_lines.Head
|
||
while e_line is not None:
|
||
eb: str = e_line.Buf
|
||
if eb is not None:
|
||
a_name: str = _peep_alloc_name(eb, pool)
|
||
if a_name is not None:
|
||
# a_name 在本槽的全量行扫描中不变,strlen 只求一次(原每行各求一次)
|
||
a_name_len: t.CSizeT = string.strlen(a_name)
|
||
st_val: str = None
|
||
st_ok: int = 0
|
||
st_count: int = 0
|
||
st_line_order: int = -1
|
||
load_ltoks: str = pool.alloc(8 * 64)
|
||
load_cnt: int = 0
|
||
load_entry_order: t.CInt64T = 0
|
||
alloca_order: int = -1
|
||
order: int = 0
|
||
unsafe: int = 0
|
||
# 第一遍:统计本 alloca 的 store/load/其它引用及行序
|
||
blk_s = blk
|
||
while blk_s is not None:
|
||
l_s: rawptr[Line] = None
|
||
if blk_s.Lines is not None:
|
||
scan_lines: rawptr[GSList[Line]] = blk_s.Lines
|
||
l_s = scan_lines.Head
|
||
while l_s is not None:
|
||
sb: str = l_s.Buf
|
||
if sb is not None:
|
||
is_alloc_def: int = 0
|
||
if sb[0] == '%':
|
||
if _peep_is_alloc_of(sb, a_name, a_name_len) != 0:
|
||
is_alloc_def = 1
|
||
if is_alloc_def == 0 and _peep_last_ref_eq(sb, a_name, a_name_len) != 0:
|
||
if _peep_is_startwith(sb, "store ") != 0:
|
||
st_count += 1
|
||
if st_count == 1:
|
||
vcomma: str = string.strstr(sb + 6, ", ")
|
||
if vcomma is not None:
|
||
vlen: t.CSizeT = t.CSizeT(t.CUInt64T(vcomma) - t.CUInt64T(sb + 6))
|
||
if vlen > 0:
|
||
vbuf: str = pool.alloc(vlen + 1)
|
||
if vbuf is not None:
|
||
string.strncpy(vbuf, sb + 6, vlen)
|
||
vbuf[vlen] = '\0'
|
||
st_val = vbuf
|
||
st_ok = 1
|
||
else:
|
||
unsafe = 1
|
||
if blk_s == entry_blk:
|
||
st_line_order = order
|
||
elif string.strstr(sb, " = load ") is not None:
|
||
ls: str = string.strstr(sb, "%")
|
||
if ls is not None and load_cnt < 64:
|
||
lname_len: t.CSizeT = t.CSizeT(
|
||
t.CUInt64T(string.strstr(sb, " = load ")) - t.CUInt64T(ls))
|
||
lname: str = pool.alloc(lname_len + 1)
|
||
if lname is not None:
|
||
string.strncpy(lname, ls + 1, lname_len - 1)
|
||
lname[lname_len - 1] = '\0'
|
||
slotp: str | t.CPtr = t.CPtr(t.CUInt64T(load_ltoks) + load_cnt * 8)
|
||
slotp[0] = lname
|
||
load_cnt += 1
|
||
if blk_s == entry_blk and st_line_order < 0:
|
||
load_entry_order += 1
|
||
else:
|
||
# lname 分配失败:该 load 不会进 load_ltoks,但删除阶段
|
||
# 仍会按槽名删其 def 行,use 无法改写为 store 值 → 悬空。
|
||
# 保守放弃本 alloca 槽的 T2 转发。
|
||
unsafe = 1
|
||
else:
|
||
# 本 alloca 的 load 超过记录容量(load_ltoks=8*64,上限 64)
|
||
# 或行内无 '%' 无法解析 def 名:超出容量的 load 不会被记录,
|
||
# 但删除阶段按槽名删除全部 " = load " def 行 → 未记录 load 的
|
||
# use 未改写为 store 值,将悬空引用已删 def(llc:
|
||
# "use of undefined value",见 strcmp 短路链 >64 分支场景)。
|
||
# 只有 load 全集都被记录时才能安全删全量 def,故保守放弃
|
||
# 本 alloca 槽的整个 T2 转发(保留 alloca/store/load,IR 仍合法)。
|
||
unsafe = 1
|
||
else:
|
||
unsafe = 1
|
||
if sb is not None:
|
||
order += 1
|
||
l_s = l_s.Next
|
||
blk_s = blk_s.Next
|
||
# 判据
|
||
safe_val: str = None
|
||
if unsafe == 0 and st_count == 1 and st_ok != 0 and st_line_order >= 0 \
|
||
and load_cnt > 0 and load_entry_order == 0 and st_val is not None:
|
||
pct: str = string.strchr(st_val, '%')
|
||
if pct is None:
|
||
sp1: str = string.strchr(st_val, ' ')
|
||
if sp1 is not None and sp1[1] != 0:
|
||
safe_val = sp1 + 1
|
||
else:
|
||
safe_val = st_val
|
||
else:
|
||
if string.strchr(pct, ' ') is None and string.strchr(pct, ',') is None:
|
||
safe_val = pct
|
||
if safe_val is not None:
|
||
# 第二遍:替换 load 名引用
|
||
# safe_val 在本槽替换阶段不变,strlen 只求一次(原每行各求一次)
|
||
safe_len: t.CSizeT = string.strlen(safe_val)
|
||
li2: int = 0
|
||
while li2 < load_cnt:
|
||
slotsp: str | t.CPtr = t.CPtr(t.CUInt64T(load_ltoks) + li2 * 8)
|
||
ltok: str = slotsp[0]
|
||
if ltok is not None:
|
||
# ltok 在该 load 名的全函数行扫描中不变,strlen 只求一次
|
||
ltok_len: t.CSizeT = string.strlen(ltok)
|
||
blk_r = blk
|
||
while blk_r is not None:
|
||
lr: rawptr[Line] = None
|
||
if blk_r.Lines is not None:
|
||
repl_lines: rawptr[GSList[Line]] = blk_r.Lines
|
||
lr = repl_lines.Head
|
||
while lr is not None:
|
||
if lr.Buf is not None:
|
||
nb2: str = _peep_replace_token(pool, lr.Buf, ltok, safe_val,
|
||
ltok_len, safe_len)
|
||
if nb2 is not None and nb2 is not lr.Buf:
|
||
lr.Buf = nb2
|
||
lr = lr.Next
|
||
blk_r = blk_r.Next
|
||
li2 += 1
|
||
# 删除:alloca def / 唯一 store / 全部 load 行
|
||
blk_d = blk
|
||
while blk_d is not None:
|
||
ld: rawptr[Line] = None
|
||
if blk_d.Lines is not None:
|
||
kill_lines: rawptr[GSList[Line]] = blk_d.Lines
|
||
ld = kill_lines.Head
|
||
while ld is not None:
|
||
db: str = ld.Buf
|
||
if db is not None:
|
||
kill: int = 0
|
||
if db[0] == '%' and db[1] == a_name[0]:
|
||
if _peep_is_alloc_of(db, a_name, a_name_len) != 0:
|
||
kill = 1
|
||
if kill == 0 and _peep_last_ref_eq(db, a_name, a_name_len) != 0:
|
||
if _peep_is_startwith(db, "store ") != 0 \
|
||
or string.strstr(db, " = load ") is not None:
|
||
kill = 1
|
||
if kill != 0:
|
||
ld.Buf = None
|
||
ld = ld.Next
|
||
blk_d = blk_d.Next
|
||
e_line = e_line.Next
|
||
|
||
# ---------- T3: no-op bitcast 消除 ----------
|
||
_peep_noop_bitcast(func, pool)
|
||
|
||
|
||
def _peep_noop_bitcast(func: rawptr[Function], pool: rawptr[memhub.MemBuddy]):
|
||
"""T3: 消除 no-op bitcast(bitcast <T> %x to <T>,两侧类型相同)。
|
||
|
||
生成端(虚调用/联合位宽转换)会产生同类型恒等 bitcast(如
|
||
`%6 = bitcast i8* %ok_msg to i8*`),纯冗余指令。把 %x 的所有引用
|
||
替换为源操作数(%SSA/@global/常量 token)后删除该行。
|
||
保守限制:类型与操作数均为不含空格的简单 token。
|
||
"""
|
||
if func is None or pool is None or func.Blocks is None:
|
||
return
|
||
# 单趟前向扫描:命中合法候选 → 全函数替换引用 + 删行,游标从下一行续扫;
|
||
# 遇到首个不满足条件的 bitcast 即停止。与原实现"每删一行后从块头整函数
|
||
# 重扫、遇无效候选即 return"的删除结果逐一等价(替换只改操作数 token,
|
||
# 不会把游标之前的非候选行变成候选),只是消除了 O(K·C) 的重扫二次方。
|
||
blk: rawptr[BasicBlock] = func.Blocks.Head
|
||
while blk is not None:
|
||
ln: rawptr[Line] = None
|
||
if blk.Lines is not None:
|
||
ln = blk.Lines.Head
|
||
while ln is not None:
|
||
nxt: rawptr[Line] = ln.Next
|
||
if ln.Buf is not None:
|
||
eqpos: str = string.strstr(ln.Buf, " = bitcast ")
|
||
if eqpos is not None:
|
||
nlen: t.CSizeT = t.CSizeT(t.CUInt64T(eqpos) - t.CUInt64T(ln.Buf)) - 1
|
||
nm_buf: str = None
|
||
src_buf: str = None
|
||
if nlen > 0:
|
||
after: str = eqpos + 11 # 跳过 " = bitcast "
|
||
sp1: str = string.strchr(after, ' ')
|
||
tpos: str = string.strstr(after, " to ")
|
||
if sp1 is not None and tpos is not None \
|
||
and t.CUInt64T(sp1) < t.CUInt64T(tpos):
|
||
tyA_len: t.CSizeT = t.CSizeT(t.CUInt64T(sp1) - t.CUInt64T(after))
|
||
src_st: str = sp1 + 1
|
||
src_len: t.CSizeT = t.CSizeT(t.CUInt64T(tpos) - t.CUInt64T(src_st))
|
||
tyB: str = tpos + 4
|
||
tyB_len: t.CSizeT = string.strlen(tyB)
|
||
if src_len > 0 and string.strchr(tyB, ' ') is None \
|
||
and t.CUInt64T(string.strchr(src_st, ' ')) == t.CUInt64T(tpos) \
|
||
and tyA_len == tyB_len \
|
||
and string.strncmp(after, tyB, tyA_len) == 0:
|
||
nm_buf = pool.alloc(nlen + 1)
|
||
if nm_buf is not None:
|
||
string.strncpy(nm_buf, ln.Buf + 1, nlen)
|
||
nm_buf[nlen] = '\0'
|
||
src_buf = pool.alloc(src_len + 1)
|
||
if src_buf is not None:
|
||
string.strncpy(src_buf, src_st, src_len)
|
||
src_buf[src_len] = '\0'
|
||
if nm_buf is None or src_buf is None:
|
||
# 首个不满足条件的 bitcast:停止(与原语义一致)
|
||
return
|
||
# 全函数把 %nm 的引用替换为源操作数
|
||
# nm_buf/src_buf 在全函数行扫描中不变,strlen 只求一次(原每行各求一次)
|
||
nm_buf_len: t.CSizeT = string.strlen(nm_buf)
|
||
src_buf_len: t.CSizeT = string.strlen(src_buf)
|
||
blk_r: rawptr[BasicBlock] = func.Blocks.Head
|
||
while blk_r is not None:
|
||
if blk_r.Lines is not None:
|
||
ln2: rawptr[Line] = blk_r.Lines.Head
|
||
while ln2 is not None:
|
||
if ln2.Buf is not None:
|
||
rb: str = _peep_replace_token(pool, ln2.Buf, nm_buf, src_buf,
|
||
nm_buf_len, src_buf_len)
|
||
if rb is not None and rb is not ln2.Buf:
|
||
ln2.Buf = rb
|
||
ln2.Len = string.strlen(rb)
|
||
ln2 = ln2.Next
|
||
blk_r = blk_r.Next
|
||
ln.Buf = None
|
||
ln = nxt
|
||
blk = blk.Next
|
||
|
||
|
||
# ============================================================
|
||
# FunctionPrint: 将函数序列化为 IR 文本
|
||
#
|
||
# 格式:
|
||
# define <ret_ty> @<name>(<params>) {
|
||
# <block_name>:
|
||
# <instruction>
|
||
# <instruction>
|
||
# <block_name>:
|
||
# <instruction>
|
||
# }
|
||
# ============================================================
|
||
def FunctionPrint(buf: str, size: t.CSizeT,
|
||
func: rawptr[Function], pool: rawptr[memhub.MemBuddy]):
|
||
"""将函数 func 序列化为 IR 文本写入 buf"""
|
||
if func is None or buf is None or size == 0: return
|
||
|
||
# 先本地窥孔(ret zeroinitializer→0/null、入口槽 store→load 转发、
|
||
# no-op bitcast 消除):窥孔按"名称一致性"匹配、删除行置 Buf=None;
|
||
# RenumberSSA 遍历时已跳过 Buf=None 行 → 窥孔之后再重编号,输出 SSA
|
||
# 编号连续(窥孔删行后不再出现 %1→%5 空洞,文件更紧凑)。
|
||
# RenumberSSA 内部会跳过 declare 函数(IsDeclared==1),无需在此判断。
|
||
if func.IsDeclared == 0 and G_PeepEnable != 0:
|
||
_peephole_opt(func, pool)
|
||
|
||
RenumberSSA(func, pool)
|
||
|
||
ty_buf: str = 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:
|
||
# 泛型特化类方法(如 GSList[NameEntry].__before_init__):多个模块各自
|
||
# 特化生成同名定义 → ld.lld duplicate symbol warning。特化方法代码由
|
||
# 相同模板+类型参数生成,定义相同,输出 linkonce_odr 让链接器合并。
|
||
if func.Name is not None and string.strchr(func.Name, 91) is not None:
|
||
_append_cstr(buf, size, "define linkonce_odr ")
|
||
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 遍历)
|
||
params: rawptr[GSList[Param]] = func.Params
|
||
cur: rawptr[Param] = None
|
||
if params is not None:
|
||
cur = params.Head
|
||
first: int = 1
|
||
cur_steps: t.CSizeT = 0
|
||
while cur is not None:
|
||
cur_steps += 1
|
||
if cur_steps > IR_DUMP_CHAIN_LIMIT:
|
||
if params is not None:
|
||
stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params.Count=%zu,遍历超 %d 步,print 终止)\n",
|
||
func.Name, params.Count, IR_DUMP_CHAIN_LIMIT)
|
||
else:
|
||
stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params=None,遍历超 %d 步,print 终止)\n",
|
||
func.Name, IR_DUMP_CHAIN_LIMIT)
|
||
stdlib.exit(1)
|
||
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 params is not None and 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)
|
||
# B5:字符串属性(section 等)必须用属性组 #N 引用(LLVM 不接受内联
|
||
# section="..."),函数行输出 #N,组定义在函数尾/模块中输出。
|
||
if func.AttrGroup is not None and func.AttrGroupId > 0:
|
||
ag_ref: str = pool.alloc(32)
|
||
if ag_ref is not None:
|
||
viperlib.snprintf(ag_ref, 32, " #%d", func.AttrGroupId)
|
||
_append_cstr(buf, size, ag_ref)
|
||
|
||
if func.IsDeclared == 1:
|
||
_append_cstr(buf, size, "\n")
|
||
_append_attr_group_def(buf, size, pool, func)
|
||
return
|
||
|
||
# 函数体
|
||
_append_cstr(buf, size, " {\n")
|
||
|
||
blocks: rawptr[GSList[BasicBlock]] = func.Blocks
|
||
blk: rawptr[BasicBlock] = None
|
||
if blocks is not None:
|
||
blk = blocks.Head
|
||
blk_steps: t.CSizeT = 0
|
||
while blk is not None:
|
||
blk_steps += 1
|
||
if blk_steps > IR_DUMP_CHAIN_LIMIT:
|
||
stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,print 终止)\n", func.Name)
|
||
stdlib.exit(1)
|
||
# 块标签
|
||
if blk.Name is not None:
|
||
_append_cstr(buf, size, blk.Name)
|
||
_append_cstr(buf, size, ":\n")
|
||
# 指令行(从 GSList[Line].Head 遍历)
|
||
line: rawptr[Line] = None
|
||
if blk.Lines is not None:
|
||
lines: rawptr[GSList[Line]] = blk.Lines
|
||
line = lines.Head
|
||
has_lines: int = 0
|
||
line_steps: t.CSizeT = 0
|
||
while line is not None:
|
||
line_steps += 1
|
||
if line_steps > IR_DUMP_CHAIN_LIMIT:
|
||
stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,print 终止)\n",
|
||
func.Name, blk.Name)
|
||
stdlib.exit(1)
|
||
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")
|
||
_append_attr_group_def(buf, size, pool, func)
|
||
|
||
|
||
def _append_attr_group_def(buf: str, size: t.CSizeT,
|
||
pool: rawptr[memhub.MemBuddy],
|
||
func: rawptr[Function]):
|
||
"""输出字符串属性组定义: attributes #N = { "section"="..." }"""
|
||
if func is None or func.AttrGroup is None or func.AttrGroupId <= 0:
|
||
return
|
||
ag_buf: str = pool.alloc(192)
|
||
if ag_buf is None:
|
||
return
|
||
viperlib.snprintf(ag_buf, 192, "attributes #%d = { %s }\n",
|
||
func.AttrGroupId, func.AttrGroup)
|
||
_append_cstr(buf, size, ag_buf)
|
||
|
||
|
||
# ============================================================
|
||
# 内部辅助
|
||
# ============================================================
|
||
# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位,
|
||
# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。
|
||
# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。
|
||
_g_last_dst: t.CUInt64T = 0
|
||
_g_last_pos: t.CSizeT = 0
|
||
|
||
def _append_cstr(dst: str, dst_size: t.CSizeT, src: str):
|
||
"""将 C 字符串 src 追加到 dst 末尾"""
|
||
global _g_last_dst, _g_last_pos
|
||
if dst is None or src is None: return
|
||
dlen: t.CSizeT = 0
|
||
if _g_last_dst == t.CUInt64T(dst):
|
||
dlen = _g_last_pos
|
||
# 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部
|
||
if dlen > 0 and dst[0] == '\0':
|
||
dlen = 0
|
||
# 缓存一致性校验:缓存结尾必须仍是当前文本末端(末端 NUL 且前一字符非 NUL)。
|
||
# 多个模块轮流写同一 buf、或 pool 复用地址残留旧内容时缓存 pos 会过期,
|
||
# 追加落到首个 NUL 之后内容"看不见"(LLvmLiteTest Test5/9 根因)。失败回退 strlen。
|
||
if dlen == 0:
|
||
if dst[0] != '\0':
|
||
dlen = string.strlen(dst)
|
||
elif dst[dlen] != '\0' or dst[dlen - 1] == '\0':
|
||
dlen = string.strlen(dst)
|
||
else:
|
||
dlen = string.strlen(dst)
|
||
_g_last_dst = t.CUInt64T(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'
|
||
_g_last_pos = dlen + i
|
||
|
||
|
||
# ============================================================
|
||
# 字段访问器(供其他模块绕过 stub 类型限制使用)
|
||
#
|
||
# 跨模块编译时,使用方模块只有 Function/Param 的 stub 类型
|
||
# (字段不足且字段类型可能错误),导致 TransPyC 静默跳过
|
||
# 字段访问(不生成 GEP,不报错),字段值永远为 null。
|
||
#
|
||
# 这些访问器在拥有完整类型定义的本模块内执行,能正确访问
|
||
# 所有字段。其他模块通过函数调用获取字段值,绕过 stub 限制。
|
||
# ============================================================
|
||
def function_get_name(func: rawptr[Function]) -> str:
|
||
"""获取函数名"""
|
||
if func is None:
|
||
return None
|
||
return func.Name
|
||
|
||
|
||
def function_get_name_len(func: rawptr[Function]) -> t.CSizeT:
|
||
"""获取函数名长度(创建时一次算好,恒等于 strlen(Name))
|
||
|
||
对齐 function_get_name 的跨模块 stub 绕过模式:使用方模块只有 Function
|
||
的 stub 类型(字段不足且字段类型可能错误),直接读 Name/NameLen 字段会被
|
||
TransPyC 静默跳过;本访问器在拥有完整类型定义的本模块内取字段,跨模块经
|
||
函数调用取得长度,避免对同一函数名反复 strlen(全程序 strlen 热点)。
|
||
"""
|
||
if func is None:
|
||
return 0
|
||
return func.NameLen
|
||
|
||
|
||
def function_get_ret_ty(func: rawptr[Function]) -> rawptr[LLVMType]:
|
||
"""获取函数返回类型"""
|
||
if func is None:
|
||
return None
|
||
return func.RetTy
|
||
|
||
|
||
def function_get_params(func: rawptr[Function]) -> rawptr[GSList[Param]]:
|
||
"""获取函数参数链表容器"""
|
||
if func is None:
|
||
return None
|
||
return func.Params
|
||
|
||
|
||
def function_get_param_head(func: rawptr[Function]) -> rawptr[Param]:
|
||
"""获取函数参数链表头节点(一步到位,避免调用方访问 GSList.Head)"""
|
||
if func is None:
|
||
return None
|
||
params: rawptr[GSList[Param]] = func.Params
|
||
if params is None:
|
||
return None
|
||
return params.Head
|
||
|
||
|
||
def function_get_next(func: rawptr[Function]) -> rawptr[Function]:
|
||
"""获取链表下一个函数"""
|
||
if func is None:
|
||
return None
|
||
return func.Next
|
||
|
||
|
||
def function_is_declared(func: rawptr[Function]) -> int:
|
||
"""获取函数是否为声明(1=declare 仅声明, 0=define 定义)"""
|
||
if func is None:
|
||
return 0
|
||
return func.IsDeclared
|
||
|
||
|
||
def param_get_name(param: rawptr[Param]) -> str:
|
||
"""获取参数名"""
|
||
if param is None:
|
||
return None
|
||
return param.Name
|
||
|
||
|
||
def param_get_ty(param: rawptr[Param]) -> rawptr[LLVMType]:
|
||
"""获取参数类型"""
|
||
if param is None:
|
||
return None
|
||
return param.Ty
|
||
|
||
|
||
def param_get_next(param: rawptr[Param]) -> rawptr[Param]:
|
||
"""获取链表下一个参数"""
|
||
if param is None:
|
||
return None
|
||
return param.Next
|