Files
TransPyC/includes/llvmlite/__function.vp
T

1339 lines
55 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
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
# ============================================================
# 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: rowptr[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: rowptr[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: rowptr[LLVMType] # 返回类型
Params: rowptr[GSList[Param]] # 参数链表容器
Blocks: rowptr[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)
# ============================================================
# 工厂函数
# ============================================================
# Line 硬编码大小: Next(8) + Buf(8) + Len(8) = 24 字节
LINE_SIZE: t.CDefine = 24
def new_line(pool: rowptr[memhub.MemBuddy], text: str) -> rowptr[Line]:
"""创建一个指令行,复制 text 到新分配的缓冲区"""
ptr: rowptr[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: rowptr[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: rowptr[memhub.MemBuddy], name: str) -> rowptr[BasicBlock]:
"""创建一个基本块"""
ptr: rowptr[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: rowptr[memhub.MemBuddy], ty: rowptr[LLVMType],
name: str) -> rowptr[Param]:
"""创建一个函数参数"""
ptr: rowptr[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) = 72 字节
FUNC_SIZE: t.CDefine = 72
def new_function(pool: rowptr[memhub.MemBuddy], name: str,
ret_ty: rowptr[LLVMType]) -> rowptr[Function]:
"""创建一个函数"""
ptr: rowptr[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
return ptr
# ============================================================
# 链表操作(委托 GSList.append)
# ============================================================
def function_add_param(func: rowptr[Function], param: rowptr[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: rowptr[GSList[Param]] = func.Params
if params is None: return
params.append(param)
def function_add_block(func: rowptr[Function], block: rowptr[BasicBlock]):
"""将基本块追加到函数块链表"""
if func is None or block is None: return
blocks: rowptr[GSList[BasicBlock]] = func.Blocks
if blocks is None: return
blocks.append(block)
def function_move_block_to_end(func: rowptr[Function], block: rowptr[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: rowptr[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: rowptr[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: rowptr[BasicBlock], line: rowptr[Line]):
"""将指令行追加到基本块"""
if block is None or line is None: return
lines: rowptr[GSList[Line]] = block.Lines
if lines is None: return
lines.append(line)
def block_set_terminated(block: rowptr[BasicBlock], val: int):
"""设置块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)"""
if block is None: return
block.IsTerminated = val
def block_get_terminated(block: rowptr[BasicBlock]) -> int:
"""获取块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)"""
if block is None:
return 1
return block.IsTerminated
def block_append_text(pool: rowptr[memhub.MemBuddy], block: rowptr[BasicBlock],
text: str):
"""创建指令行并追加到基本块"""
if block is None or text is None: return
line: rowptr[Line] = new_line(pool, text)
if line is not None:
block_append_line(block, line)
def block_insert_text_before_terminator(pool: rowptr[memhub.MemBuddy],
block: rowptr[BasicBlock],
text: str):
"""创建指令行并插入到基本块终止指令之前
如果块未终止,追加到末尾。
如果块已终止(有 br/ret),在终止指令之前插入,确保 alloca 等指令
不出现在终止指令之后。
"""
if block is None or text is None: return
if block.Lines is None: return
line: rowptr[Line] = new_line(pool, text)
if line is None: return
# 块未终止:直接追加
if block.IsTerminated == 0:
block_append_line(block, line)
return
# 块已终止:在 Tail(终止指令)之前插入
lines: rowptr[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: rowptr[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: rowptr[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: rowptr[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: rowptr[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)
使用 (rowptr[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: rowptr[int] = (rowptr[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)
使用 (rowptr[int])(uint64) 强转 + c.DerefAs 解引用赋值。
TPC 已修复类型联合强转,此写法可用。
"""
if map_arr is None:
return
byte_off: t.CUInt64T = t.CUInt64T(map_arr) + idx * 4
elem_ptr: rowptr[int] = (rowptr[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: rowptr[Function]) -> int:
"""遍历所有 BB 指令行,找出最大的 %N 数字;无数字 SSA 名返回 -1"""
max_n: int = -1
if func is None or func.Blocks is None:
return max_n
blocks: rowptr[GSList[BasicBlock]] = func.Blocks
blk: rowptr[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: rowptr[GSList[Line]] = blk.Lines
line: rowptr[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: rowptr[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: rowptr[Function], pool: rowptr[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: rowptr[GSList[BasicBlock]] = func.Blocks
blk: rowptr[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: rowptr[GSList[Line]] = blk.Lines
line: rowptr[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: rowptr[GSList[Line]] = blk.Lines
line: rowptr[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: rowptr[memhub.MemBuddy],
line_buf: str, tok: str, repl: str) -> str:
"""将 line_buf 中独立记号 %<tok> 替换为 repl,返回新缓冲(无变化返回原指针)"""
if line_buf is None or tok is None or repl is None:
return line_buf
tlen: t.CSizeT = string.strlen(tok)
if tlen == 0:
return line_buf
rlen: t.CSizeT = string.strlen(repl)
ln: t.CSizeT = string.strlen(line_buf)
# 预扫是否命中
hit: int = 0
i: t.CSizeT = 0
while i + tlen <= ln:
if line_buf[i] == '%' and string.strncmp(line_buf + i + 1, tok, tlen) == 0:
nxt: int = 0
if i + 1 + tlen < ln:
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/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 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) -> int:
"""行内最后一个 '%name' 记号是否等于 a_name(store/load 的指针操作数在行尾)"""
if sb is None or a_name is None:
return 0
r: str = None
probe: str = sb
while True:
hit: str = string.strchr(probe, '%')
if hit is None:
break
r = hit
probe = hit + 1
if r is None:
return 0
nl: t.CSizeT = string.strlen(a_name)
if 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: rowptr[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 _peephole_opt(func: rowptr[Function], pool: rowptr[memhub.MemBuddy]):
"""文本层本地窥孔:T1 ret zeroinitializer;T2 入口槽 store→load 转发"""
if func is None or func.Blocks is None or pool is None:
return
# 注:TPC 对 "变量 = 指针.字段.成员" 两级自动解引用链会静默丢弃赋值
# (编译出的 IR 直接读未初始化栈变量 → 随机栈错误)。必须拆成单级:
# 先取字段到局部 rowptr,再判空后取 .Head(与 FunctionPrint 同一模式)。
func_blocks: rowptr[GSList[BasicBlock]] = func.Blocks
blk: rowptr[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: rowptr[Line] = None
if blk_t1.Lines is not None:
t1_lines: rowptr[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: rowptr[BasicBlock] = blk
if entry_blk is None:
return
entry_lines: rowptr[GSList[Line]] = entry_blk.Lines
if entry_lines is None:
return
e_line: rowptr[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:
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: rowptr[Line] = None
if blk_s.Lines is not None:
scan_lines: rowptr[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] == '%':
tmp_nm: str = _peep_alloc_name(sb, pool)
if tmp_nm is not None and string.strcmp(tmp_nm, a_name) == 0:
is_alloc_def = 1
if is_alloc_def == 0 and _peep_last_ref_eq(sb, a_name) != 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 名引用
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:
blk_r = blk
while blk_r is not None:
lr: rowptr[Line] = None
if blk_r.Lines is not None:
repl_lines: rowptr[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)
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: rowptr[Line] = None
if blk_d.Lines is not None:
kill_lines: rowptr[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
dnm: str = None
if db[0] == '%':
dnm = _peep_alloc_name(db, pool)
if dnm is not None and string.strcmp(dnm, a_name) == 0:
kill = 1
if kill == 0 and _peep_last_ref_eq(db, a_name) != 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: rowptr[Function], pool: rowptr[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
again: int = 1
while again != 0:
again = 0
hit_line: rowptr[Line] = None
nm_buf: str = None
src_buf: str = None
blk: rowptr[BasicBlock] = func.Blocks.Head
while blk is not None and hit_line is None:
if blk.Lines is not None:
ln: rowptr[Line] = blk.Lines.Head
while ln is not None and hit_line is None:
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
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'
hit_line = ln
ln = ln.Next
blk = blk.Next
if hit_line is None or nm_buf is None or src_buf is None:
return
# 全函数把 %nm 的引用替换为源操作数
blk = func.Blocks.Head
while blk is not None:
if blk.Lines is not None:
ln2: rowptr[Line] = blk.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)
if rb is not None and rb is not ln2.Buf:
ln2.Buf = rb
ln2.Len = string.strlen(rb)
ln2 = ln2.Next
blk = blk.Next
hit_line.Buf = None
again = 1
# ============================================================
# FunctionPrint: 将函数序列化为 IR 文本
#
# 格式:
# define <ret_ty> @<name>(<params>) {
# <block_name>:
# <instruction>
# <instruction>
# <block_name>:
# <instruction>
# }
# ============================================================
def FunctionPrint(buf: str, size: t.CSizeT,
func: rowptr[Function], pool: rowptr[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:
_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: rowptr[GSList[Param]] = func.Params
cur: rowptr[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: rowptr[GSList[BasicBlock]] = func.Blocks
blk: rowptr[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: rowptr[Line] = None
if blk.Lines is not None:
lines: rowptr[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: rowptr[memhub.MemBuddy],
func: rowptr[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
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: rowptr[Function]) -> str:
"""获取函数名"""
if func is None:
return None
return func.Name
def function_get_ret_ty(func: rowptr[Function]) -> rowptr[LLVMType]:
"""获取函数返回类型"""
if func is None:
return None
return func.RetTy
def function_get_params(func: rowptr[Function]) -> rowptr[GSList[Param]]:
"""获取函数参数链表容器"""
if func is None:
return None
return func.Params
def function_get_param_head(func: rowptr[Function]) -> rowptr[Param]:
"""获取函数参数链表头节点(一步到位,避免调用方访问 GSList.Head)"""
if func is None:
return None
params: rowptr[GSList[Param]] = func.Params
if params is None:
return None
return params.Head
def function_get_next(func: rowptr[Function]) -> rowptr[Function]:
"""获取链表下一个函数"""
if func is None:
return None
return func.Next
def function_is_declared(func: rowptr[Function]) -> int:
"""获取函数是否为声明(1=declare 仅声明, 0=define 定义)"""
if func is None:
return 0
return func.IsDeclared
def param_get_name(param: rowptr[Param]) -> str:
"""获取参数名"""
if param is None:
return None
return param.Name
def param_get_ty(param: rowptr[Param]) -> rowptr[LLVMType]:
"""获取参数类型"""
if param is None:
return None
return param.Ty
def param_get_next(param: rowptr[Param]) -> rowptr[Param]:
"""获取链表下一个参数"""
if param is None:
return None
return param.Next