- __module.vp: 新增 DINode/DINamedMeta 类、LLVMModule 追加 6 个 DI 字段(96->144B)、module_add_di_node/module_add_named_metadata 及实例方法、DI_NODE_SIZE/DI_NAMED_SIZE - __function.vp: Function 追加 DbgRef(80->88B) + function_set_dbg - __builder.vp: IRBuilder 追加 DbgLoc(32->40B) + set/clear_debug_location 方法 + 顶层 builder_set/clear_debug_location - __init__.vp: 导出上述 7 个 DI 名字,恢复与 llvmlite 的逐名同形(215=215) - MLIR 打印阶段忽略 DI:LLVMModulePrint 不输出 !N 元数据、_emit 不读 DbgLoc,保持 MLIR 语法合法(DI 只记录不输出) - README: 记录 DI 为「同形保留/只记录不输出」,名字数更新为 215
3417 lines
135 KiB
Plaintext
3417 lines
135 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,
|
||
MlirTypeText, MlirTypeTextIsPointer, MlirFail, MlirSetContextLine,
|
||
)
|
||
|
||
|
||
# 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 会话根因分析)。
|
||
# ============================================================
|
||
novirtual class Line(GSListNode[Line]):
|
||
Buf: str # 行文本(NUL 结尾)
|
||
Len: sizet # 文本长度(不含 NUL)
|
||
|
||
|
||
# ============================================================
|
||
# BasicBlock: 基本块
|
||
#
|
||
# 持有名称 + 指令行链表 + 终止指令标志。
|
||
# 继承 GSListNode[BasicBlock] 获取强类型 Next: BasicBlock|CPtr
|
||
# Lines 为 GSList[Line] 指针,提供 O(1) 追加。
|
||
#
|
||
# 注意: 必须加 @t.NoVTable(理由同 Line)。
|
||
# ============================================================
|
||
novirtual 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)。
|
||
# ============================================================
|
||
novirtual 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)。
|
||
# ============================================================
|
||
novirtual 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: sizet # 函数名长度(创建时一次算好,恒等于 strlen(Name);追加在末尾,既有字段偏移不变)
|
||
DbgRef: str # DISubprogram ref(同形保留;MLIR 打印阶段忽略,如 "!3")
|
||
|
||
|
||
# ============================================================
|
||
# 工厂函数
|
||
# ============================================================
|
||
# Line 硬编码大小: Next(8) + Buf(8) + Len(8) = 24 字节
|
||
LINE_SIZE: t.CDefine = 24
|
||
|
||
def new_line(pool: 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: sizet = 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: memhub.MemBuddy*, list_size: sizet) -> t.CPtr:
|
||
"""从 pool 分配并零初始化一个 GSList 容器(Head/Tail/Count 全零)"""
|
||
gsl: t.CPtr = pool.alloc(list_size)
|
||
if gsl is None: return None
|
||
string.memset(gsl, 0, list_size)
|
||
return gsl
|
||
|
||
|
||
# BasicBlock 硬编码大小: Next(8) + Name(8) + Lines(8) + IsTerminated(4) = 32 字节
|
||
BLOCK_SIZE: t.CDefine = 32
|
||
|
||
def new_basic_block(pool: memhub.MemBuddy*, 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: 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) + DbgRef(8) = 88 字节(与 llvmlite 同布局)
|
||
FUNC_SIZE: t.CDefine = 88
|
||
|
||
def new_function(pool: 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: 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: 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
|
||
|
||
|
||
def function_set_dbg(func: rawptr[Function], ref: str):
|
||
"""Set the function-level debug ref (DISubprogram node ref, e.g. "!3").
|
||
|
||
同形保留:MLIR 打印阶段忽略该引用(FunctionPrint 不输出 !dbg),
|
||
故本调用只记录模型、不改变输出。ref=None 清除。
|
||
"""
|
||
if func is None: return
|
||
func.DbgRef = ref
|
||
|
||
|
||
# ============================================================
|
||
# _ll_name_needs_quote - 检查符号名在 MLIR 中是否需要 @"…" 引号形式
|
||
#
|
||
# ⚠ 判据与 LLVM **不同**(实测 mlir-opt 22.1.8):
|
||
# - 裸标识符**首字符必须是字母或 '_'**:数字、'.'、'$' 开头都必须加引号。
|
||
# ⇒ TPV 的 {sha1}.{name} 全部以十六进制数字开头,**一律需要引号**;
|
||
# ⇒ 字符串常量全局 @.str 以 '.' 开头,也需要引号。
|
||
# - 后续字符仅允许 [A-Za-z0-9_$] 与 '.';
|
||
# - '-' 在 MLIR 裸名中**非法**(LLVM 允许),实测报 "expected '('"。
|
||
# - 含 '['/']'(泛型特化名)同样非法。
|
||
# 本判据取保守方向:拿不准就加引号(@"…" 对任意名字都合法)。
|
||
# ============================================================
|
||
def _ll_name_needs_quote(name: str) -> int:
|
||
"""MLIR 符号名裸写判据:不满足 [A-Za-z_][A-Za-z0-9_$.]* 即返回 1(需引号)"""
|
||
if name is None:
|
||
return 0
|
||
name_len: sizet = string.strlen(name)
|
||
if name_len == 0:
|
||
return 1
|
||
c0: char = name[0]
|
||
if not ((c0 >= 'a' and c0 <= 'z') or (c0 >= 'A' and c0 <= 'Z') or c0 == '_'):
|
||
return 1
|
||
i: sizet = 1
|
||
while i < name_len:
|
||
ch: char = name[i]
|
||
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 == '.'):
|
||
return 1
|
||
i += 1
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# _MlirSymRefAppend - 输出符号引用(统一引号口径)
|
||
#
|
||
# name **不含** 前导 '@';已引号形式(首字符 '"')原样输出。
|
||
# 用于 llvm.mlir.addressof 等"操作数里出现符号"的位置,与
|
||
# _MlirFuncSig / 调用目标 / 全局名 三处共用同一判据。
|
||
# ============================================================
|
||
def _MlirSymRefAppend(out: str, outsz: sizet, name: str):
|
||
if name is None or name[0] == '\0':
|
||
MlirFail("MLIR 渲染: 空符号名")
|
||
if name[0] == '"':
|
||
_append_cstr(out, outsz, "@")
|
||
_append_cstr(out, outsz, name)
|
||
return
|
||
if _ll_name_needs_quote(name) != 0:
|
||
_append_cstr(out, outsz, "@\"")
|
||
_append_cstr(out, outsz, name)
|
||
_append_cstr(out, outsz, "\"")
|
||
return
|
||
_append_cstr(out, outsz, "@")
|
||
_append_cstr(out, outsz, name)
|
||
|
||
|
||
# ============================================================
|
||
# 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: char) -> int:
|
||
"""判断字符是否是 '0'-'9'
|
||
|
||
ch 是值类型 (char = 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: sizet = 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: sizet = 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: memhub.MemBuddy*,
|
||
map_arr: str,
|
||
max_n: int) -> str:
|
||
"""构建新文本,将 %N 替换为映射后的新编号
|
||
|
||
逐字符扫描:遇到 %N(数字)查映射表替换,其他字符原样复制。
|
||
返回新分配的缓冲区(调用方无需释放旧 Buf,pool 统一管理)。
|
||
"""
|
||
src_len: sizet = string.strlen(text)
|
||
# 新缓冲区:最坏情况每个数字变长(位数增加),分配 src_len*2 + 64
|
||
new_size: sizet = 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: sizet = 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: sizet = 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: 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: sizet = (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: sizet = 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: sizet = 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: sizet = 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: memhub.MemBuddy*,
|
||
line_buf: str, tok: str, repl: str,
|
||
tlen: sizet, rlen: sizet) -> 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: sizet = 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: sizet = 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: sizet = 0
|
||
oq: sizet = 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: sizet = 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: sizet) -> 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: 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: sizet = sizet(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: sizet) -> 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: 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: sizet = sizet(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: sizet = 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: sizet = sizet(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: sizet = sizet(
|
||
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 = (str | 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: sizet = string.strlen(safe_val)
|
||
li2: int = 0
|
||
while li2 < load_cnt:
|
||
slotsp: str | t.CPtr = (str | t.CPtr)(t.CUInt64T(load_ltoks) + li2 * 8)
|
||
ltok: str = slotsp[0]
|
||
if ltok is not None:
|
||
# ltok 在该 load 名的全函数行扫描中不变,strlen 只求一次
|
||
ltok_len: sizet = 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: 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: sizet = sizet(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: sizet = sizet(t.CUInt64T(sp1) - t.CUInt64T(after))
|
||
src_st: str = sp1 + 1
|
||
src_len: sizet = sizet(t.CUInt64T(tpos) - t.CUInt64T(src_st))
|
||
tyB: str = tpos + 4
|
||
tyB_len: sizet = 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: sizet = string.strlen(nm_buf)
|
||
src_buf_len: sizet = 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
|
||
|
||
|
||
# ============================================================
|
||
# MLIR(llvm 方言) 渲染阶段
|
||
#
|
||
# 位置:FunctionPrint 内部。builders 一行不改,仍按 LLVM 语法把指令文本写进
|
||
# Line;本阶段把中间文本重新解析,把 MLIR 特有的三类差异集中在此解决:
|
||
# 1) 常量物化 —— MLIR 操作数不能是字面量:非 SSA 操作数(整数/浮点/
|
||
# null/zeroinitializer/@符号)在 entry 块开头生成
|
||
# llvm.mlir.constant / llvm.mlir.zero / llvm.mlir.addressof
|
||
# op 并分配 %cN 名(同函数内同 (类型,文本) 只物化一次)
|
||
# 2) phi→块参数 —— 块首连续 phi 变成块参数 ^blk(%0: i32),phi 行不再输出;
|
||
# 跳转到该块的 br/cond_br 按源块匹配入边追加实参
|
||
# 3) 不透明指针 —— 所有指针统一 !llvm.ptr(TypePrint/MlirTypeText 已收敛)
|
||
#
|
||
# 两遍扫描:pass1 只登记(常量表 / bitcast no-op 别名表)+ 形态校验;
|
||
# pass2 才真正写文本(此时常量表已完整,entry 开头的常量 op 可先输出)。
|
||
# ============================================================
|
||
|
||
# 表格容量
|
||
MLIR_CONST_CAP: t.CDefine = 512
|
||
MLIR_ALIAS_CAP: t.CDefine = 512
|
||
|
||
# 渲染阶段:1=扫描(登记) 2=发射(写文本)
|
||
_MLIR_Pass: int = 0
|
||
_MLIR_Pool: memhub.MemBuddy* = None
|
||
_MLIR_Func: rawptr[Function] = None
|
||
_MLIR_SrcBlk: rawptr[BasicBlock] = None
|
||
|
||
# 常量表:(MLIR 类型文本, 常量文本) -> 物化出的 SSA 名
|
||
_MLIR_ConstTy: t.CPtr = None
|
||
_MLIR_ConstVal: t.CPtr = None
|
||
_MLIR_ConstName: t.CPtr = None
|
||
_MLIR_ConstCount: int = 0
|
||
|
||
# bitcast no-op 别名表 + cmpxchg 结果绑定表:结果名 -> 源操作数 / extractvalue 结果名
|
||
# (cmpxchg 的 extractvalue 结果名由结果名确定性派生,不再用计数器,见 _MlirCxResName)
|
||
_MLIR_AliasFrom: t.CPtr = None
|
||
_MLIR_AliasTo: t.CPtr = None
|
||
_MLIR_AliasCount: int = 0
|
||
|
||
# 当前正在渲染的中间指令文本行(_MlirRenderLine 入口赋值)。
|
||
# 供类型文本转换失败的报文追加行上下文:MlirFail 是即时终止、拿不到调用栈,
|
||
# 故把行文本寄存到 __types.vp(经 MlirSetContextLine),由报错处一并打印。
|
||
_MLIR_CurLine: str = None
|
||
|
||
# 解析期 scratch 缓冲(每次 FunctionPrint 分配一次,全程复用;
|
||
# 所有需要跨行存活的文本在入表/入别名表时都会 dup,见 _mlir_dup)
|
||
_SC_Op: str = None
|
||
_SC_Tb: str = None
|
||
_SC_Ty: str = None
|
||
_SC_Ty2: str = None
|
||
_SC_V1: str = None
|
||
_SC_V2: str = None
|
||
_SC_V3: str = None
|
||
_SC_Callee: str = None
|
||
_SC_Args: str = None
|
||
_SC_ArgTs: str = None
|
||
_SC_IdxV: str = None
|
||
_SC_IdxT: str = None
|
||
_SC_Mt: str = None
|
||
_SC_Tmp: str = None
|
||
_SC_Tmp2: str = None
|
||
_SC_P1: str = None
|
||
_SC_P2: str = None
|
||
_SC_B1: str = None
|
||
_SC_B2: str = None
|
||
_SC_Buf: str = None
|
||
|
||
|
||
def _mlir_slotp(buf: t.CPtr, idx: int) -> str | t.CPtr:
|
||
"""表格槽位地址(buf + idx*8)"""
|
||
return (str | t.CPtr)(t.CUInt64T(buf) + idx * 8)
|
||
|
||
|
||
def _mlir_dup(s: str) -> str:
|
||
"""把文本复制到池(用于需要跨行存活的表项)"""
|
||
if s is None:
|
||
return None
|
||
n: sizet = string.strlen(s)
|
||
b: str = _MLIR_Pool.alloc(n + 1)
|
||
if b is None:
|
||
MlirFail("MLIR 渲染: 文本 dup 分配失败")
|
||
string.strncpy(b, s, n)
|
||
b[n] = '\0'
|
||
return b
|
||
|
||
|
||
def _MlirTablesInit(pool: memhub.MemBuddy*):
|
||
"""初始化两个表和解析用 scratch 缓冲"""
|
||
global _MLIR_Pool, _MLIR_Pass, _MLIR_ConstTy, _MLIR_ConstVal, _MLIR_ConstName, _MLIR_ConstCount, _MLIR_AliasFrom, _MLIR_AliasTo, _MLIR_AliasCount, _SC_Op, _SC_Tb, _SC_Ty, _SC_Ty2, _SC_V1, _SC_V2, _SC_V3, _SC_Callee, _SC_Args, _SC_ArgTs, _SC_IdxV, _SC_IdxT, _SC_Mt, _SC_Tmp, _SC_Tmp2, _SC_P1, _SC_P2, _SC_B1, _SC_B2, _SC_Buf
|
||
if pool is None:
|
||
MlirFail("MLIR 渲染: 内存池为空")
|
||
_MLIR_Pool = pool
|
||
_MLIR_Pass = 0
|
||
_MLIR_ConstCount = 0
|
||
_MLIR_AliasCount = 0
|
||
_MLIR_ConstTy = pool.alloc(MLIR_CONST_CAP * 8)
|
||
_MLIR_ConstVal = pool.alloc(MLIR_CONST_CAP * 8)
|
||
_MLIR_ConstName = pool.alloc(MLIR_CONST_CAP * 8)
|
||
_MLIR_AliasFrom = pool.alloc(MLIR_ALIAS_CAP * 8)
|
||
_MLIR_AliasTo = pool.alloc(MLIR_ALIAS_CAP * 8)
|
||
if _MLIR_ConstTy is None or _MLIR_ConstVal is None or _MLIR_ConstName is None \
|
||
or _MLIR_AliasFrom is None or _MLIR_AliasTo is None:
|
||
MlirFail("MLIR 渲染: 常量/别名表分配失败")
|
||
string.memset(_MLIR_ConstTy, 0, MLIR_CONST_CAP * 8)
|
||
string.memset(_MLIR_ConstVal, 0, MLIR_CONST_CAP * 8)
|
||
string.memset(_MLIR_ConstName, 0, MLIR_CONST_CAP * 8)
|
||
string.memset(_MLIR_AliasFrom, 0, MLIR_ALIAS_CAP * 8)
|
||
string.memset(_MLIR_AliasTo, 0, MLIR_ALIAS_CAP * 8)
|
||
_SC_Op = pool.alloc(64)
|
||
_SC_Tb = pool.alloc(512)
|
||
_SC_Ty = pool.alloc(512)
|
||
_SC_Ty2 = pool.alloc(512)
|
||
_SC_V1 = pool.alloc(512)
|
||
_SC_V2 = pool.alloc(512)
|
||
_SC_V3 = pool.alloc(512)
|
||
_SC_Callee = pool.alloc(512)
|
||
_SC_Args = pool.alloc(2048)
|
||
_SC_ArgTs = pool.alloc(2048)
|
||
_SC_IdxV = pool.alloc(1024)
|
||
_SC_IdxT = pool.alloc(1024)
|
||
_SC_Mt = pool.alloc(512)
|
||
_SC_Tmp = pool.alloc(4096)
|
||
_SC_Tmp2 = pool.alloc(1024)
|
||
_SC_P1 = pool.alloc(512)
|
||
_SC_P2 = pool.alloc(512)
|
||
_SC_B1 = pool.alloc(512)
|
||
_SC_B2 = pool.alloc(512)
|
||
_SC_Buf = pool.alloc(512)
|
||
if _SC_Op is None or _SC_Ty is None or _SC_Mt is None or _SC_Tmp is None:
|
||
MlirFail("MLIR 渲染: 解析 scratch 分配失败")
|
||
_SC_Op[0] = '\0'
|
||
_SC_Ty[0] = '\0'
|
||
_SC_Mt[0] = '\0'
|
||
_SC_Tmp[0] = '\0'
|
||
|
||
|
||
# ============================================================
|
||
# 常量表 / 别名表
|
||
# ============================================================
|
||
def _MlirAliasLookup(name: str) -> str:
|
||
"""查别名表,未命中返回 None"""
|
||
if name is None:
|
||
return None
|
||
i: int = 0
|
||
while i < _MLIR_AliasCount:
|
||
sp: str | t.CPtr = _mlir_slotp(_MLIR_AliasFrom, i)
|
||
if sp[0] is not None and string.strcmp(sp[0], name) == 0:
|
||
st: str | t.CPtr = _mlir_slotp(_MLIR_AliasTo, i)
|
||
return st[0]
|
||
i += 1
|
||
return None
|
||
|
||
|
||
def _MlirAliasAdd(from_name: str, to_name: str):
|
||
"""登记 no-op bitcast 别名(from 由 to 取代)"""
|
||
global _MLIR_AliasCount
|
||
if from_name is None or to_name is None:
|
||
return
|
||
if _MLIR_AliasCount >= MLIR_ALIAS_CAP:
|
||
MlirFail("MLIR 渲染: bitcast 别名表溢出")
|
||
sp: str | t.CPtr = _mlir_slotp(_MLIR_AliasFrom, _MLIR_AliasCount)
|
||
st: str | t.CPtr = _mlir_slotp(_MLIR_AliasTo, _MLIR_AliasCount)
|
||
sp[0] = _mlir_dup(from_name)
|
||
st[0] = _mlir_dup(to_name)
|
||
_MLIR_AliasCount += 1
|
||
|
||
|
||
def _MlirResolve(name: str) -> str:
|
||
"""别名链解析(bitcast no-op 的结果名 -> 真实操作数)"""
|
||
if name is None:
|
||
return None
|
||
cur: str = name
|
||
g: int = 0
|
||
while g < 64:
|
||
nx: str = _MlirAliasLookup(cur)
|
||
if nx is None:
|
||
return cur
|
||
cur = nx
|
||
g += 1
|
||
return cur
|
||
|
||
|
||
def _MlirConstGet(tytext: str, valtext: str) -> str:
|
||
"""取(或新建)常量的物化名 %cN"""
|
||
global _MLIR_ConstCount
|
||
i: int = 0
|
||
while i < _MLIR_ConstCount:
|
||
sp: str | t.CPtr = _mlir_slotp(_MLIR_ConstTy, i)
|
||
if sp[0] is not None and string.strcmp(sp[0], tytext) == 0:
|
||
sv: str | t.CPtr = _mlir_slotp(_MLIR_ConstVal, i)
|
||
if sv[0] is not None and string.strcmp(sv[0], valtext) == 0:
|
||
sn: str | t.CPtr = _mlir_slotp(_MLIR_ConstName, i)
|
||
return sn[0]
|
||
i += 1
|
||
if _MLIR_Pass == 2:
|
||
# pass1 已遍历全部指令行,此处不应出现新常量
|
||
MlirFail("MLIR 渲染: 发射阶段出现未物化常量(扫描遗漏)")
|
||
if _MLIR_ConstCount >= MLIR_CONST_CAP:
|
||
MlirFail("MLIR 渲染: 常量表溢出")
|
||
nm: str = _MLIR_Pool.alloc(32)
|
||
if nm is None:
|
||
MlirFail("MLIR 渲染: 常量名分配失败")
|
||
viperlib.snprintf(nm, 32, "%%c%d", _MLIR_ConstCount)
|
||
sp2: str | t.CPtr = _mlir_slotp(_MLIR_ConstTy, _MLIR_ConstCount)
|
||
sv2: str | t.CPtr = _mlir_slotp(_MLIR_ConstVal, _MLIR_ConstCount)
|
||
sn2: str | t.CPtr = _mlir_slotp(_MLIR_ConstName, _MLIR_ConstCount)
|
||
sp2[0] = _mlir_dup(tytext)
|
||
sv2[0] = _mlir_dup(valtext)
|
||
sn2[0] = nm
|
||
_MLIR_ConstCount += 1
|
||
return nm
|
||
|
||
|
||
def _MlirCxResName(resname: str, suffix: str) -> str:
|
||
"""cmpxchg 的 extractvalue 结果名:由 struct 结果名**确定性派生**
|
||
|
||
MLIR 裸 SSA 名首字符必须是字母或 `_`(mlir-opt 22.1.8 实测:`%0_v` 报
|
||
"expected '=' after SSA name"),故统一在结果名正文前加 `_`:
|
||
`%0` → `%_0_v`(旧值 [0])/ `%_0_ok`(成功位 [1])
|
||
`%a` → `%_a_v` / `%_a_ok`
|
||
名字与结果名一一对应、不含计数器 ⇒ pass1(登记别名)与 pass2(发射)
|
||
生成的名字必然逐字相同,引用不会悬空。
|
||
"""
|
||
nm: str = _MLIR_Pool.alloc(64)
|
||
if nm is None:
|
||
MlirFail("MLIR 渲染: cmpxchg 结果名分配失败")
|
||
nm[0] = '\0'
|
||
_append_cstr(nm, 64, "%_")
|
||
_append_cstr(nm, 64, resname + 1)
|
||
_append_cstr(nm, 64, suffix)
|
||
return nm
|
||
|
||
|
||
def _MlirParseHexU64(s: str) -> t.CUInt64T:
|
||
"""解析 0x 十六进制字面量为 u64"""
|
||
p: str = s
|
||
if p[0] == '0' and (p[1] == 'x' or p[1] == 'X'):
|
||
p = p + 2
|
||
v: t.CUInt64T = 0
|
||
g: int = 0
|
||
while p[0] != '\0' and g < 32:
|
||
ch: int = p[0]
|
||
d: int = -1
|
||
if ch >= '0' and ch <= '9':
|
||
d = ch - 48
|
||
elif ch >= 'a' and ch <= 'f':
|
||
d = ch - 87
|
||
elif ch >= 'A' and ch <= 'F':
|
||
d = ch - 55
|
||
else:
|
||
break
|
||
v = v * 16 + t.CUInt64T(d)
|
||
p = p + 1
|
||
g += 1
|
||
return v
|
||
|
||
|
||
def _MlirFloatLiteral(val: str, mt: str) -> str:
|
||
"""浮点常量文本 → MLIR 十进制科学计数法文本(f16/f128 不支持)"""
|
||
if val[0] == '0' and (val[1] == 'x' or val[1] == 'X'):
|
||
bits: t.CUInt64T = _MlirParseHexU64(val)
|
||
buf: str = _MLIR_Pool.alloc(64)
|
||
if buf is None:
|
||
MlirFail("MLIR 渲染: 浮点常量缓冲分配失败")
|
||
if mt[1] == '3' and mt[2] == '2':
|
||
f32v: t.CFloat = 0.0
|
||
c.DerefAs((rawptr[t.CUInt32T])(c.Addr(f32v)), t.CUInt32T(bits))
|
||
dv32: t.CDouble = t.CDouble(f32v)
|
||
viperlib.snprintf(buf, 64, "%e", dv32)
|
||
return buf
|
||
if mt[1] == '6' and mt[2] == '4':
|
||
f64v: t.CDouble = 0.0
|
||
c.DerefAs((rawptr[t.CUInt64T])(c.Addr(f64v)), bits)
|
||
viperlib.snprintf(buf, 64, "%e", f64v)
|
||
return buf
|
||
MlirFail("MLIR 渲染: v1 不支持 f16/f128 十六进制浮点常量物化")
|
||
# 已是十进制文本:原样使用
|
||
return val
|
||
|
||
|
||
def _MlirEmitOneConst(out: str, outsz: sizet, mt: str, val: str, nm: str):
|
||
"""发射一条常量物化 op"""
|
||
_append_cstr(out, outsz, " ")
|
||
_append_cstr(out, outsz, nm)
|
||
if val[0] == '@':
|
||
# 符号引用(全局/函数地址):走统一引号口径
|
||
# (LLVM 与 MLIR 裸名判据不同:MLIR 不允许数字/'.'开头,也不允许 '-')
|
||
_append_cstr(out, outsz, " = llvm.mlir.addressof ")
|
||
_MlirSymRefAppend(out, outsz, val + 1)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, mt)
|
||
elif string.strcmp(val, "null") == 0 or string.strcmp(val, "zeroinitializer") == 0:
|
||
_append_cstr(out, outsz, " = llvm.mlir.zero : ")
|
||
_append_cstr(out, outsz, mt)
|
||
else:
|
||
lit: str = val
|
||
if mt[0] == 'f':
|
||
lit = _MlirFloatLiteral(val, mt)
|
||
elif mt[0] != 'i':
|
||
MlirFail("MLIR 渲染: 不支持的常量操作数形态")
|
||
_append_cstr(out, outsz, " = llvm.mlir.constant(")
|
||
_append_cstr(out, outsz, lit)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, mt)
|
||
_append_cstr(out, outsz, ") : ")
|
||
_append_cstr(out, outsz, mt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirEmitConsts(out: str, outsz: sizet):
|
||
"""按登记顺序在 entry 块开头发射全部常量物化 op"""
|
||
i: int = 0
|
||
while i < _MLIR_ConstCount:
|
||
st: str | t.CPtr = _mlir_slotp(_MLIR_ConstTy, i)
|
||
sv: str | t.CPtr = _mlir_slotp(_MLIR_ConstVal, i)
|
||
sn: str | t.CPtr = _mlir_slotp(_MLIR_ConstName, i)
|
||
_MlirEmitOneConst(out, outsz, st[0], sv[0], sn[0])
|
||
i += 1
|
||
|
||
|
||
# ============================================================
|
||
# 中间指令文本的解析原语
|
||
# ============================================================
|
||
def _mlir_ws(p: str) -> str:
|
||
"""跳过空白(空格/制表/换行)"""
|
||
if p is None:
|
||
return None
|
||
while p[0] == ' ' or p[0] == '\t' or p[0] == '\n' or p[0] == '\r':
|
||
p = p + 1
|
||
return p
|
||
|
||
|
||
def _mlir_delim(ch: int) -> int:
|
||
"""判断字符是否结束一个记号"""
|
||
if ch == 0 or ch == 32 or ch == 9 or ch == 10 or ch == 13:
|
||
return 1
|
||
if ch == ',' or ch == '(' or ch == ')' or ch == '[' or ch == ']' \
|
||
or ch == ':' or ch == '=':
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _mlir_read_value(p: str, out: str, outsz: sizet) -> str:
|
||
"""读一个值记号(%name / @"name" / %"name" / 字面量),返回新的游标"""
|
||
p = _mlir_ws(p)
|
||
n: sizet = 0
|
||
if p[0] == '\0':
|
||
out[0] = '\0'
|
||
return p
|
||
if p[0] == '%' or p[0] == '@':
|
||
if n + 1 < outsz:
|
||
out[n] = p[0]
|
||
n += 1
|
||
p = p + 1
|
||
if p[0] == '"':
|
||
if n + 1 < outsz:
|
||
out[n] = '"'
|
||
n += 1
|
||
p = p + 1
|
||
while p[0] != '\0' and p[0] != '"':
|
||
if n + 1 < outsz:
|
||
out[n] = p[0]
|
||
n += 1
|
||
p = p + 1
|
||
if p[0] == '"':
|
||
if n + 1 < outsz:
|
||
out[n] = '"'
|
||
n += 1
|
||
p = p + 1
|
||
out[n] = '\0'
|
||
return p
|
||
while p[0] != '\0' and _mlir_delim(p[0]) == 0:
|
||
if n + 1 < outsz:
|
||
out[n] = p[0]
|
||
n += 1
|
||
p = p + 1
|
||
out[n] = '\0'
|
||
return p
|
||
|
||
|
||
def _mlir_read_type(p: str, out: str, outsz: sizet, nofunc: int = 0) -> str:
|
||
"""读一个类型记号(MLIR 形态 !llvm.… 或标量 iN/fN,兼容 LLVM 聚合形态)
|
||
|
||
括号平衡覆盖 `[] {} () <>`:MLIR 的 !llvm.array<…>/!llvm.struct<(…)> 里含
|
||
空格与逗号,必须靠深度计数整体读出;无括号类型(!llvm.ptr)遇到顶层分隔符结束。
|
||
|
||
nofunc=1 时**不吞并**紧随其后的 ` (…)` 函数类型后缀:call 的返回类型位置
|
||
上,`(…)`(LLVM 词法)是**被调函数的参数类型表**而不是返回类型的一部分,
|
||
必须留给 _MlirCall 的变参分支处理(见该处注释)。
|
||
"""
|
||
p = _mlir_ws(p)
|
||
n: sizet = 0
|
||
if p[0] == '\0':
|
||
out[0] = '\0'
|
||
return p
|
||
c0: int = p[0]
|
||
if c0 == '!' or c0 == '[' or c0 == '{' or c0 == '(':
|
||
d: int = 0
|
||
while p[0] != '\0':
|
||
ch: int = p[0]
|
||
if ch == '[' or ch == '{' or ch == '(' or ch == '<':
|
||
d += 1
|
||
elif ch == ']' or ch == '}' or ch == ')' or ch == '>':
|
||
if n + 1 < outsz:
|
||
out[n] = ch
|
||
n += 1
|
||
p = p + 1
|
||
d -= 1
|
||
if d <= 0:
|
||
break
|
||
continue
|
||
elif d == 0 and (ch == ' ' or ch == ',' or ch == '\t'
|
||
or ch == '\n' or ch == '\r'):
|
||
# 顶层分隔符:无括号类型(如 !llvm.ptr)到此结束
|
||
break
|
||
if n + 1 < outsz:
|
||
out[n] = ch
|
||
n += 1
|
||
p = p + 1
|
||
while p[0] == '*':
|
||
if n + 1 < outsz:
|
||
out[n] = '*'
|
||
n += 1
|
||
p = p + 1
|
||
out[n] = '\0'
|
||
return p
|
||
if c0 == '%':
|
||
p = _mlir_read_value(p, out, outsz)
|
||
n = string.strlen(out)
|
||
while p[0] == '*':
|
||
if n + 1 < outsz:
|
||
out[n] = '*'
|
||
n += 1
|
||
p = p + 1
|
||
out[n] = '\0'
|
||
return p
|
||
# 普通词(i32 / i8* / void / double …)
|
||
while p[0] != '\0' and _mlir_delim(p[0]) == 0:
|
||
if n + 1 < outsz:
|
||
out[n] = p[0]
|
||
n += 1
|
||
p = p + 1
|
||
# 函数类型后缀 " ( … )"(nofunc=1 时不吞并,见函数 docstring)
|
||
q: str = _mlir_ws(p)
|
||
if nofunc == 0 and q[0] == '(':
|
||
if n + 1 < outsz:
|
||
out[n] = ' '
|
||
n += 1
|
||
p = q
|
||
d2: int = 0
|
||
while p[0] != '\0':
|
||
ch2: int = p[0]
|
||
if ch2 == '(':
|
||
d2 += 1
|
||
elif ch2 == ')':
|
||
if n + 1 < outsz:
|
||
out[n] = ch2
|
||
n += 1
|
||
p = p + 1
|
||
d2 -= 1
|
||
if d2 <= 0:
|
||
break
|
||
continue
|
||
if n + 1 < outsz:
|
||
out[n] = ch2
|
||
n += 1
|
||
p = p + 1
|
||
while p[0] == '*':
|
||
if n + 1 < outsz:
|
||
out[n] = '*'
|
||
n += 1
|
||
p = p + 1
|
||
out[n] = '\0'
|
||
return p
|
||
|
||
|
||
def _mlir_in_set(ob: str, setstr: str) -> int:
|
||
"""判断 opcode 是否属于 "|a|b|c|" 形式的集合"""
|
||
tb: str = _SC_Tb
|
||
tb[0] = '\0'
|
||
string.strcat(tb, "|")
|
||
string.strcat(tb, ob)
|
||
string.strcat(tb, "|")
|
||
if string.strstr(setstr, tb) is not None:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _MlirBlkRef(t: str) -> str:
|
||
"""块引用文本:去掉前导 '%'(MLIR 块用 ^name)"""
|
||
if t is None:
|
||
return ""
|
||
if t[0] == '%':
|
||
return t + 1
|
||
return t
|
||
|
||
|
||
def _MlirFindBlock(func: rawptr[Function], name: str) -> rawptr[BasicBlock]:
|
||
"""按块名(不含 %)在函数内查找基本块"""
|
||
if func is None or func.Blocks is None or name is None:
|
||
return None
|
||
blocks: rawptr[GSList[BasicBlock]] = func.Blocks
|
||
blk: rawptr[BasicBlock] = blocks.Head
|
||
steps: int = 0
|
||
while blk is not None:
|
||
steps += 1
|
||
if steps > IR_DUMP_CHAIN_LIMIT:
|
||
MlirFail("MLIR 渲染: 块链疑似成环")
|
||
if blk.Name is not None and string.strcmp(blk.Name, name) == 0:
|
||
return blk
|
||
blk = blk.Next
|
||
return None
|
||
|
||
|
||
def _MlirIsPhiLine(text: str) -> int:
|
||
"""判断是否为 phi 行"""
|
||
if text is None:
|
||
return 0
|
||
if string.strstr(text, " = phi ") is not None:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# 操作数渲染(含常量物化)
|
||
# ============================================================
|
||
def _MlirOperand(val: str, tyllvm: str, out: str, outsz: sizet) -> str:
|
||
"""渲染一个操作数,返回其最终文本
|
||
|
||
SSA 值 → 别名解析后的 %name;
|
||
非 SSA(字面量/符号引用)→ 物化后的 %cN(scan 阶段顺带登记)。
|
||
"""
|
||
if val is None or val[0] == '\0':
|
||
MlirFail("MLIR 渲染: 操作数为空")
|
||
if val[0] == '%':
|
||
r: str = _MlirResolve(val)
|
||
_append_cstr(out, outsz, r)
|
||
return r
|
||
mt: str = _SC_Mt
|
||
mt[0] = '\0'
|
||
MlirTypeText(mt, 512, tyllvm, _MLIR_Pool)
|
||
nm: str = _MlirConstGet(mt, val)
|
||
_append_cstr(out, outsz, nm)
|
||
return nm
|
||
|
||
|
||
# ============================================================
|
||
# phi 解析
|
||
# ============================================================
|
||
def _MlirPhiScan(text: str):
|
||
"""扫描 phi 行:登记所有入边常量(scan 阶段专用)"""
|
||
p: str = _mlir_ws(text)
|
||
tb: str = _SC_Buf
|
||
p = _mlir_read_value(p, tb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == '=':
|
||
p = _mlir_ws(p + 1)
|
||
ob: str = _SC_Op
|
||
p = _mlir_read_value(p, ob, 64)
|
||
if string.strcmp(ob, "phi") != 0:
|
||
return
|
||
tyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, tyb, 512)
|
||
g: int = 0
|
||
while p[0] != '\0':
|
||
g += 1
|
||
if g > 512:
|
||
MlirFail("MLIR 渲染: phi 入边过多")
|
||
p = _mlir_ws(p)
|
||
if p[0] != '[':
|
||
break
|
||
p = _mlir_ws(p + 1)
|
||
vb: str = _SC_P1
|
||
p = _mlir_read_value(p, vb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
bb: str = _SC_P2
|
||
p = _mlir_read_value(p, bb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ']':
|
||
p = p + 1
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = p + 1
|
||
# 登记该入边值(常量/符号引用需要物化)
|
||
_MlirOperand(vb, tyb, None, 0)
|
||
|
||
|
||
def _MlirPhiParam(text: str) -> int:
|
||
"""取 phi 的结果名与类型作为块参数;成功写 _SC_B1(名)/_SC_B2(类型) 返回 1"""
|
||
p: str = _mlir_ws(text)
|
||
nm: str = _SC_B1
|
||
p = _mlir_read_value(p, nm, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] != '=':
|
||
return 0
|
||
p = _mlir_ws(p + 1)
|
||
ob: str = _SC_Op
|
||
p = _mlir_read_value(p, ob, 64)
|
||
if string.strcmp(ob, "phi") != 0:
|
||
return 0
|
||
tyb: str = _SC_B2
|
||
_mlir_read_type(p, tyb, 512)
|
||
return 1
|
||
|
||
|
||
def _MlirPhiFindIncoming(text: str, src: rawptr[BasicBlock]) -> int:
|
||
"""找与源块匹配的 phi 入边;成功写 _SC_B1(值)/_SC_B2(类型) 返回 1"""
|
||
p: str = _mlir_ws(text)
|
||
tb: str = _SC_Buf
|
||
p = _mlir_read_value(p, tb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == '=':
|
||
p = _mlir_ws(p + 1)
|
||
ob: str = _SC_Op
|
||
p = _mlir_read_value(p, ob, 64)
|
||
if string.strcmp(ob, "phi") != 0:
|
||
return 0
|
||
tyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, tyb, 512)
|
||
srcn: str = None
|
||
if src is not None:
|
||
srcn = src.Name
|
||
g: int = 0
|
||
while p[0] != '\0':
|
||
g += 1
|
||
if g > 512:
|
||
MlirFail("MLIR 渲染: phi 入边过多")
|
||
p = _mlir_ws(p)
|
||
if p[0] != '[':
|
||
break
|
||
p = _mlir_ws(p + 1)
|
||
vb: str = _SC_P1
|
||
p = _mlir_read_value(p, vb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
bb: str = _SC_P2
|
||
p = _mlir_read_value(p, bb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ']':
|
||
p = p + 1
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = p + 1
|
||
if srcn is not None:
|
||
bn: str = bb
|
||
if bn[0] == '%':
|
||
bn = bn + 1
|
||
if string.strcmp(bn, srcn) == 0:
|
||
string.strcpy(_SC_B1, vb)
|
||
string.strcpy(_SC_B2, tyb)
|
||
return 1
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# 块参数 / 块实参
|
||
# ============================================================
|
||
def _MlirBlockHasPhi(blk: rawptr[BasicBlock]) -> int:
|
||
"""判断块首是否有 phi(即该块是否带块参数)"""
|
||
if blk is None or blk.Lines is None:
|
||
return 0
|
||
lines: rawptr[GSList[Line]] = blk.Lines
|
||
ln: rawptr[Line] = lines.Head
|
||
while ln is not None:
|
||
if ln.Buf is not None:
|
||
if _MlirIsPhiLine(ln.Buf) != 0:
|
||
return 1
|
||
return 0
|
||
ln = ln.Next
|
||
return 0
|
||
|
||
|
||
def _MlirBlockOperands(out: str, outsz: sizet,
|
||
target: rawptr[BasicBlock], src: rawptr[BasicBlock]):
|
||
"""跳转到 target 时按其块参数生成实参: (%v : ty, …)
|
||
|
||
入边按源块匹配 phi;匹配不到 → 响亮报错(绝不静默丢实参)。
|
||
"""
|
||
if target is None or src is None:
|
||
return
|
||
if _MlirBlockHasPhi(target) == 0:
|
||
return
|
||
tmp: str = _SC_Tmp
|
||
tmp[0] = '\0'
|
||
lines: rawptr[GSList[Line]] = target.Lines
|
||
ln: rawptr[Line] = lines.Head
|
||
cnt: int = 0
|
||
while ln is not None:
|
||
if ln.Buf is None:
|
||
ln = ln.Next
|
||
continue
|
||
if _MlirIsPhiLine(ln.Buf) == 0:
|
||
break
|
||
if _MlirPhiFindIncoming(ln.Buf, src) == 0:
|
||
MlirFail("MLIR 渲染: 跳转目标块带 phi,但找不到与源块匹配的入边")
|
||
if cnt > 0:
|
||
_append_cstr(tmp, 4096, ", ")
|
||
_MlirOperand(_SC_B1, _SC_B2, tmp, 4096)
|
||
_append_cstr(tmp, 4096, " : ")
|
||
mt: str = _SC_Mt
|
||
mt[0] = '\0'
|
||
MlirTypeText(mt, 512, _SC_B2, _MLIR_Pool)
|
||
_append_cstr(tmp, 4096, mt)
|
||
cnt += 1
|
||
ln = ln.Next
|
||
if cnt == 0:
|
||
MlirFail("MLIR 渲染: 目标块带 phi 但未生成块实参")
|
||
_append_cstr(out, outsz, "(")
|
||
_append_cstr(out, outsz, tmp)
|
||
_append_cstr(out, outsz, ")")
|
||
|
||
|
||
def _MlirEmitBlockHeader(out: str, outsz: sizet, blk: rawptr[BasicBlock]):
|
||
"""输出非 entry 块头: ^name(%p: ty, …):"""
|
||
_append_cstr(out, outsz, "^")
|
||
if blk.Name is not None:
|
||
_append_cstr(out, outsz, blk.Name)
|
||
if _MlirBlockHasPhi(blk) != 0:
|
||
tmp: str = _SC_Tmp
|
||
tmp[0] = '\0'
|
||
lines: rawptr[GSList[Line]] = blk.Lines
|
||
ln: rawptr[Line] = lines.Head
|
||
cnt: int = 0
|
||
while ln is not None:
|
||
if ln.Buf is None:
|
||
ln = ln.Next
|
||
continue
|
||
if _MlirIsPhiLine(ln.Buf) == 0:
|
||
break
|
||
if _MlirPhiParam(ln.Buf) != 0:
|
||
if cnt > 0:
|
||
_append_cstr(tmp, 4096, ", ")
|
||
_append_cstr(tmp, 4096, _SC_B1)
|
||
_append_cstr(tmp, 4096, ": ")
|
||
mt: str = _SC_Mt
|
||
mt[0] = '\0'
|
||
MlirTypeText(mt, 512, _SC_B2, _MLIR_Pool)
|
||
_append_cstr(tmp, 4096, mt)
|
||
cnt += 1
|
||
ln = ln.Next
|
||
if cnt == 0:
|
||
MlirFail("MLIR 渲染: 块首 phi 解析失败")
|
||
_append_cstr(out, outsz, "(")
|
||
_append_cstr(out, outsz, tmp)
|
||
_append_cstr(out, outsz, ")")
|
||
_append_cstr(out, outsz, ":\n")
|
||
|
||
|
||
def _MlirEmitBlockLines(out: str, outsz: sizet, blk: rawptr[BasicBlock]):
|
||
"""输出块内指令(跳过块首连续 phi —— 它们已成为块参数)"""
|
||
if blk is None or blk.Lines is None:
|
||
return
|
||
lines: rawptr[GSList[Line]] = blk.Lines
|
||
ln: rawptr[Line] = lines.Head
|
||
skipping: int = 1
|
||
steps: int = 0
|
||
while ln is not None:
|
||
steps += 1
|
||
if steps > IR_DUMP_CHAIN_LIMIT:
|
||
MlirFail("MLIR 渲染: 块指令链疑似成环")
|
||
if ln.Buf is not None:
|
||
if skipping != 0 and _MlirIsPhiLine(ln.Buf) != 0:
|
||
ln = ln.Next
|
||
continue
|
||
skipping = 0
|
||
_MlirRenderLine(ln.Buf, out, outsz)
|
||
ln = ln.Next
|
||
|
||
|
||
def _MlirCheckEntryNoPhi(blk: rawptr[BasicBlock]):
|
||
"""entry 块不允许 phi(无前驱,无法成为块参数)"""
|
||
if _MlirBlockHasPhi(blk) != 0:
|
||
MlirFail("MLIR 渲染: entry 块不允许 phi(MLIR entry 块不接受块参数)")
|
||
|
||
|
||
# ============================================================
|
||
# 各指令的 MLIR 渲染
|
||
# ============================================================
|
||
def _MlirEmitRes(out: str, outsz: sizet, resname: str):
|
||
"""输出 " [%N = ]" 前缀"""
|
||
_append_cstr(out, outsz, " ")
|
||
if resname is not None:
|
||
_append_cstr(out, outsz, resname)
|
||
_append_cstr(out, outsz, " = ")
|
||
|
||
|
||
def _MlirBinop(p: str, resname: str, ob: str, out: str, outsz: sizet):
|
||
"""整数/浮点二元运算: llvm.<op> %a, %b : ty"""
|
||
tyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, tyb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
lb: str = _SC_V1
|
||
p = _mlir_read_value(p, lb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
rb: str = _SC_V2
|
||
_mlir_read_value(p, rb, 512)
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.")
|
||
_append_cstr(out, outsz, ob)
|
||
_append_cstr(out, outsz, " ")
|
||
_MlirOperand(lb, tyb, out, outsz)
|
||
_append_cstr(out, outsz, ", ")
|
||
_MlirOperand(rb, tyb, out, outsz)
|
||
mt: str = _SC_Mt
|
||
mt[0] = '\0'
|
||
MlirTypeText(mt, 512, tyb, _MLIR_Pool)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, mt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirFneg(p: str, resname: str, out: str, outsz: sizet):
|
||
"""fneg: llvm.fneg %a : ty"""
|
||
tyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, tyb, 512)
|
||
vb: str = _SC_V1
|
||
_mlir_read_value(p, vb, 512)
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.fneg ")
|
||
_MlirOperand(vb, tyb, out, outsz)
|
||
mt: str = _SC_Mt
|
||
mt[0] = '\0'
|
||
MlirTypeText(mt, 512, tyb, _MLIR_Pool)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, mt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirCmp(p: str, resname: str, ob: str, out: str, outsz: sizet):
|
||
"""icmp/fcmp: llvm.icmp "sgt" %a, %b : ty
|
||
|
||
中间文本是 LLVM 语法 `icmp <谓词> <类型> <lhs>, <rhs>`——**谓词在类型之前**,
|
||
故读取顺序必须是 谓词 → 类型 → 操作数。
|
||
注意不能先读类型:那样会把谓词(如 `sgt`)当类型文本传给 _MlirTypeAppend,
|
||
直接触发"无法转换为 MLIR 类型文本"而中止(Test 4 首条指令即 icmp 的根因)。
|
||
"""
|
||
pb: str = _SC_P1
|
||
p = _mlir_read_value(p, pb, 512)
|
||
p = _mlir_ws(p)
|
||
tyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, tyb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
lb: str = _SC_V1
|
||
p = _mlir_read_value(p, lb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
rb: str = _SC_V2
|
||
_mlir_read_value(p, rb, 512)
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.")
|
||
_append_cstr(out, outsz, ob)
|
||
_append_cstr(out, outsz, " \"")
|
||
_append_cstr(out, outsz, pb)
|
||
_append_cstr(out, outsz, "\" ")
|
||
_MlirOperand(lb, tyb, out, outsz)
|
||
_append_cstr(out, outsz, ", ")
|
||
_MlirOperand(rb, tyb, out, outsz)
|
||
mt: str = _SC_Mt
|
||
mt[0] = '\0'
|
||
MlirTypeText(mt, 512, tyb, _MLIR_Pool)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, mt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirAlloca(p: str, resname: str, out: str, outsz: sizet):
|
||
"""alloca: llvm.alloca %cN x ty {alignment = N : i64} : (i32) -> !llvm.ptr
|
||
|
||
对齐属性的 MLIR 形态由 mlir-opt 22.1.8 实测确定(**不得凭记忆**):
|
||
`{alignment = 16 : i64}` → exit 0 ✓(采用)
|
||
`align(16)` / `, align 16`(LLVM 词法) → exit 1(expected ':')
|
||
`{alignment = 16 : i32}` → exit 1
|
||
("attribute 'alignment' failed to satisfy constraint: 64-bit signless
|
||
integer attribute"——必须是 **i64**)
|
||
故中间文本 `, align N` 必须转写为 `{alignment = N : i64}`。
|
||
|
||
对齐值只接受**常量十进制**且为 **2 的幂**:mlir-opt 不校验幂次
|
||
(实测 `{alignment = 3 : i64}` exit 0),但非 2 的幂在 LLVM 语义里非法,
|
||
故本层把关,非常量/非幂/超范围(> 2^30)一律响亮报错。
|
||
"""
|
||
tyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, tyb, 512)
|
||
p = _mlir_ws(p)
|
||
aligntxt: str = _SC_P2
|
||
aligntxt[0] = '\0'
|
||
if p[0] == ',':
|
||
# ", align N"(build_alloca_aligned 的中间文本形态)
|
||
p = _mlir_ws(p + 1)
|
||
if string.strncmp(p, "align", 5) != 0:
|
||
MlirFail("MLIR 渲染: alloca 只支持 align 属性(未知属性)")
|
||
p = _mlir_ws(p + 5)
|
||
nd: int = 0
|
||
while p[0] >= '0' and p[0] <= '9':
|
||
if nd + 1 >= 512:
|
||
MlirFail("MLIR 渲染: alloca 对齐值过长(超范围)")
|
||
aligntxt[nd] = p[0]
|
||
nd += 1
|
||
p = p + 1
|
||
aligntxt[nd] = '\0'
|
||
p = _mlir_ws(p)
|
||
if p[0] != '\0':
|
||
MlirFail("MLIR 渲染: alloca 的 align 之后出现多余记号")
|
||
if nd == 0:
|
||
MlirFail("MLIR 渲染: alloca 对齐值必须是常量(常量十进制,非 SSA)")
|
||
if nd > 10:
|
||
MlirFail("MLIR 渲染: alloca 对齐值超范围(须为 2 的幂且 <= 2^30)")
|
||
av: int = string.atoi(aligntxt)
|
||
if av <= 0 or av > 1073741824:
|
||
MlirFail("MLIR 渲染: alloca 对齐值超范围(须为 2 的幂且 <= 2^30)")
|
||
if (av & (av - 1)) != 0:
|
||
MlirFail("MLIR 渲染: alloca 对齐值必须是 2 的幂")
|
||
mt: str = _SC_Mt
|
||
mt[0] = '\0'
|
||
MlirTypeText(mt, 512, tyb, _MLIR_Pool)
|
||
cnt: str = _MlirConstGet("i32", "1")
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.alloca ")
|
||
_append_cstr(out, outsz, cnt)
|
||
_append_cstr(out, outsz, " x ")
|
||
_append_cstr(out, outsz, mt)
|
||
if aligntxt[0] != '\0':
|
||
_append_cstr(out, outsz, " {alignment = ")
|
||
_append_cstr(out, outsz, aligntxt)
|
||
_append_cstr(out, outsz, " : i64}")
|
||
_append_cstr(out, outsz, " : (i32) -> !llvm.ptr\n")
|
||
|
||
|
||
def _MlirLoad(p: str, resname: str, out: str, outsz: sizet):
|
||
"""load: llvm.load %p : !llvm.ptr -> ty"""
|
||
p = _mlir_ws(p)
|
||
vol: int = 0
|
||
if string.strncmp(p, "volatile ", 9) == 0:
|
||
vol = 1
|
||
p = _mlir_ws(p + 9)
|
||
tyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, tyb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
ptyb: str = _SC_Ty2
|
||
p = _mlir_read_type(p, ptyb, 512)
|
||
p = _mlir_ws(p)
|
||
pb: str = _SC_V1
|
||
_mlir_read_value(p, pb, 512)
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.load ")
|
||
if vol != 0:
|
||
_append_cstr(out, outsz, "volatile ")
|
||
_MlirOperand(pb, ptyb, out, outsz)
|
||
tmt: str = _SC_Mt
|
||
tmt[0] = '\0'
|
||
MlirTypeText(tmt, 512, tyb, _MLIR_Pool)
|
||
pmt: str = _SC_Tmp2
|
||
pmt[0] = '\0'
|
||
MlirTypeText(pmt, 1024, ptyb, _MLIR_Pool)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, pmt)
|
||
_append_cstr(out, outsz, " -> ")
|
||
_append_cstr(out, outsz, tmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirStore(p: str, out: str, outsz: sizet):
|
||
"""store: llvm.store %v, %p : ty, !llvm.ptr"""
|
||
p = _mlir_ws(p)
|
||
vol: int = 0
|
||
if string.strncmp(p, "volatile ", 9) == 0:
|
||
vol = 1
|
||
p = _mlir_ws(p + 9)
|
||
vtyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, vtyb, 512)
|
||
p = _mlir_ws(p)
|
||
vb: str = _SC_V1
|
||
p = _mlir_read_value(p, vb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
ptyb: str = _SC_Ty2
|
||
p = _mlir_read_type(p, ptyb, 512)
|
||
p = _mlir_ws(p)
|
||
pb: str = _SC_V2
|
||
_mlir_read_value(p, pb, 512)
|
||
_MlirEmitRes(out, outsz, None)
|
||
_append_cstr(out, outsz, "llvm.store ")
|
||
if vol != 0:
|
||
_append_cstr(out, outsz, "volatile ")
|
||
_MlirOperand(vb, vtyb, out, outsz)
|
||
_append_cstr(out, outsz, ", ")
|
||
_MlirOperand(pb, ptyb, out, outsz)
|
||
vmt: str = _SC_Mt
|
||
vmt[0] = '\0'
|
||
MlirTypeText(vmt, 512, vtyb, _MLIR_Pool)
|
||
pmt: str = _SC_Tmp2
|
||
pmt[0] = '\0'
|
||
MlirTypeText(pmt, 1024, ptyb, _MLIR_Pool)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, vmt)
|
||
_append_cstr(out, outsz, ", ")
|
||
_append_cstr(out, outsz, pmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirBr(p: str, out: str, outsz: sizet):
|
||
"""br:LLVM 语法里条件跳转的 opcode **也是 `br`**,靠首个记号是否为 `label` 分流
|
||
|
||
br label %x → llvm.br ^x
|
||
br i1 %c, label %a, label %b → llvm.cond_br %c, ^a, ^b
|
||
|
||
注意:build_cond_br 产出的中间文本是 `br i1 …`(**不是** `cond_br`),
|
||
故条件形态必须在此转交 _MlirCondBr;否则会被当无条件跳转,
|
||
把类型 `i1` 误当块名(产出 `llvm.br ^i1` 且丢掉条件)。
|
||
"""
|
||
p = _mlir_ws(p)
|
||
if string.strncmp(p, "label ", 6) != 0:
|
||
# 条件跳转:`<类型> <cond>, label %a, label %b`
|
||
_MlirCondBr(p, out, outsz)
|
||
return
|
||
p = _mlir_ws(p + 6)
|
||
tb: str = _SC_V1
|
||
_mlir_read_value(p, tb, 512)
|
||
br: str = _MlirBlkRef(tb)
|
||
_MlirEmitRes(out, outsz, None)
|
||
_append_cstr(out, outsz, "llvm.br ^")
|
||
_append_cstr(out, outsz, br)
|
||
_MlirBlockOperands(out, outsz, _MlirFindBlock(_MLIR_Func, br), _MLIR_SrcBlk)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirCondBr(p: str, out: str, outsz: sizet):
|
||
"""cond_br: llvm.cond_br %c, ^then(%v : ty), ^else(%v : ty)"""
|
||
ctyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, ctyb, 512)
|
||
p = _mlir_ws(p)
|
||
cb: str = _SC_V1
|
||
p = _mlir_read_value(p, cb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
if string.strncmp(p, "label ", 6) == 0:
|
||
p = _mlir_ws(p + 6)
|
||
tb: str = _SC_V2
|
||
p = _mlir_read_value(p, tb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
if string.strncmp(p, "label ", 6) == 0:
|
||
p = _mlir_ws(p + 6)
|
||
eb: str = _SC_V3
|
||
_mlir_read_value(p, eb, 512)
|
||
_MlirEmitRes(out, outsz, None)
|
||
_append_cstr(out, outsz, "llvm.cond_br ")
|
||
_MlirOperand(cb, ctyb, out, outsz)
|
||
tr: str = _MlirBlkRef(tb)
|
||
_append_cstr(out, outsz, ", ^")
|
||
_append_cstr(out, outsz, tr)
|
||
_MlirBlockOperands(out, outsz, _MlirFindBlock(_MLIR_Func, tr), _MLIR_SrcBlk)
|
||
er: str = _MlirBlkRef(eb)
|
||
_append_cstr(out, outsz, ", ^")
|
||
_append_cstr(out, outsz, er)
|
||
_MlirBlockOperands(out, outsz, _MlirFindBlock(_MLIR_Func, er), _MLIR_SrcBlk)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirRet(p: str, out: str, outsz: sizet):
|
||
"""ret: llvm.return %v : ty(void 省略操作数与类型)"""
|
||
p = _mlir_ws(p)
|
||
if string.strncmp(p, "void", 4) == 0 and (p[4] == '\0' or p[4] == ' '):
|
||
_MlirEmitRes(out, outsz, None)
|
||
_append_cstr(out, outsz, "llvm.return\n")
|
||
return
|
||
tyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, tyb, 512)
|
||
vb: str = _SC_V1
|
||
_mlir_read_value(p, vb, 512)
|
||
_MlirEmitRes(out, outsz, None)
|
||
_append_cstr(out, outsz, "llvm.return ")
|
||
_MlirOperand(vb, tyb, out, outsz)
|
||
mt: str = _SC_Mt
|
||
mt[0] = '\0'
|
||
MlirTypeText(mt, 512, tyb, _MLIR_Pool)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, mt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirUnreachable(out: str, outsz: sizet):
|
||
"""unreachable: llvm.unreachable"""
|
||
_MlirEmitRes(out, outsz, None)
|
||
_append_cstr(out, outsz, "llvm.unreachable\n")
|
||
|
||
|
||
def _MlirCall(p: str, resname: str, out: str, outsz: sizet):
|
||
"""call: llvm.call @f(%a) : (i32) -> i32 / 间接 llvm.call %fp(%a) : !llvm.ptr, (i32) -> i32
|
||
|
||
变参调用(中间文本 `call <ret> (…) @f(…)`)追加 `vararg(!llvm.func<…>)`。
|
||
"""
|
||
p = _mlir_ws(p)
|
||
variadic: int = 0
|
||
vftys: str = _SC_Tmp
|
||
vftys[0] = '\0'
|
||
rtyb: str = _SC_Ty
|
||
# 返回类型必须用 nofunc=1 读:变参调用的中间文本里,返回类型之后的 `(…)`
|
||
# 是**被调函数的参数类型表**(LLVM 词法),不是返回类型的一部分。若按默认
|
||
# 行为把它当"函数类型后缀"吞进 rtyb,则 p 落到 `@`/`%`、下面的变参分支
|
||
# 永远进不去,rtyb 变成 `i32 (!llvm.ptr, i32, ...)` 送进 MlirTypeText →
|
||
# _MlirFuncText 按逗号切参数,末片 `...` 无对应形态 → 误报
|
||
# "无法转换为 MLIR 类型文本(v1 未覆盖该形态):'...'"。
|
||
p = _mlir_read_type(p, rtyb, 512, 1)
|
||
p = _mlir_ws(p)
|
||
if string.strncmp(p, "asm", 3) == 0:
|
||
# 内联汇编:中间文本是 `[%N = ]call <ret_ty> asm [sideeffect]
|
||
# [inteldialect] "<asm>", "<constraints>"(<ty> %v, …)`(LLVM 词法),
|
||
# MLIR 形态由 _MlirAsm 转写。
|
||
_MlirAsm(p, resname, rtyb, out, outsz)
|
||
return
|
||
if p[0] == '(':
|
||
# 变参调用:中间文本是 `call <ret> (<实参类型…>, ...) @f(<实参>)`
|
||
# (builder 的 build_call is_variadic=1 形态)。
|
||
# MLIR 形态(mlir-opt 22.1.8 实测 exit 0):
|
||
# %0 = llvm.call @printf(%p, %n) vararg(!llvm.func<i32 (!llvm.ptr, i32, ...)>)
|
||
# : (!llvm.ptr, i32) -> i32
|
||
# `vararg(...)` 里是**被调函数的完整 MLIR 函数类型**(含 `...`);
|
||
# void 返回时该处返回类型写 `void`(写 `none` 实测报
|
||
# "expected var_callee_type to return void")。
|
||
# 注:vftys/calb 等缓冲用非 _SC_Ty(rtyb 占着)/非 _SC_Tmp2(实参循环用)。
|
||
variadic = 1
|
||
p = p + 1
|
||
g0: int = 0
|
||
while p[0] != '\0':
|
||
g0 += 1
|
||
if g0 > 256:
|
||
MlirFail("MLIR 渲染: 变参 call 的函数类型参数过多")
|
||
p = _mlir_ws(p)
|
||
if p[0] == ')':
|
||
p = p + 1
|
||
break
|
||
if vftys[0] != '\0':
|
||
_append_cstr(vftys, 4096, ", ")
|
||
if p[0] == '.':
|
||
# `...` 变参记号直通(不是类型,MlirTypeText 不认该记号)
|
||
_append_cstr(vftys, 4096, "...")
|
||
while p[0] == '.':
|
||
p = p + 1
|
||
else:
|
||
ftb: str = _SC_V2
|
||
ftb[0] = '\0'
|
||
p = _mlir_read_type(p, ftb, 512)
|
||
fmt: str = _SC_IdxT
|
||
fmt[0] = '\0'
|
||
MlirTypeText(fmt, 1024, ftb, _MLIR_Pool)
|
||
_append_cstr(vftys, 4096, fmt)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = p + 1
|
||
calb: str = _SC_Callee
|
||
p = _mlir_read_value(p, calb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] != '(':
|
||
MlirFail("MLIR 渲染: call 缺少实参列表")
|
||
p = p + 1
|
||
args: str = _SC_Args
|
||
argts: str = _SC_ArgTs
|
||
args[0] = '\0'
|
||
argts[0] = '\0'
|
||
nargs: int = 0
|
||
g: int = 0
|
||
while p[0] != '\0':
|
||
g += 1
|
||
if g > 256:
|
||
MlirFail("MLIR 渲染: call 实参过多")
|
||
p = _mlir_ws(p)
|
||
if p[0] == ')':
|
||
break
|
||
atyb: str = _SC_Ty2
|
||
p = _mlir_read_type(p, atyb, 512)
|
||
p = _mlir_ws(p)
|
||
avb: str = _SC_V1
|
||
p = _mlir_read_value(p, avb, 512)
|
||
if nargs > 0:
|
||
_append_cstr(args, 2048, ", ")
|
||
_append_cstr(argts, 2048, ", ")
|
||
_MlirOperand(avb, atyb, args, 2048)
|
||
amt: str = _SC_Tmp2
|
||
amt[0] = '\0'
|
||
MlirTypeText(amt, 1024, atyb, _MLIR_Pool)
|
||
_append_cstr(argts, 2048, amt)
|
||
nargs += 1
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = p + 1
|
||
rmt: str = _SC_Mt
|
||
rmt[0] = '\0'
|
||
MlirTypeText(rmt, 512, rtyb, _MLIR_Pool)
|
||
if string.strcmp(rmt, "none") == 0:
|
||
_MlirEmitRes(out, outsz, None)
|
||
else:
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.call ")
|
||
_append_cstr(out, outsz, calb)
|
||
_append_cstr(out, outsz, "(")
|
||
_append_cstr(out, outsz, args)
|
||
_append_cstr(out, outsz, ")")
|
||
if variadic != 0:
|
||
# 变参调用:vararg(被调函数的完整 MLIR 函数类型)(含 `...`)
|
||
_append_cstr(out, outsz, " vararg(!llvm.func<")
|
||
if string.strcmp(rmt, "none") == 0:
|
||
# void 返回的变参函数类型:返回类型写 `void`,不能写 `none`
|
||
_append_cstr(out, outsz, "void")
|
||
else:
|
||
_append_cstr(out, outsz, rmt)
|
||
_append_cstr(out, outsz, " (")
|
||
_append_cstr(out, outsz, vftys)
|
||
# 依次闭合三层:参数表 `)`、`!llvm.func<` 的 `>`、`vararg(` 的 `)`
|
||
# (此前漏掉 vararg 的 `)`,mlir-opt 22.1.8 逐字报 "expected ')'")
|
||
_append_cstr(out, outsz, ")>)")
|
||
_append_cstr(out, outsz, " : ")
|
||
if calb[0] != '@':
|
||
# 间接调用:被调用者是 !llvm.ptr 操作数
|
||
_append_cstr(out, outsz, "!llvm.ptr, ")
|
||
_append_cstr(out, outsz, "(")
|
||
_append_cstr(out, outsz, argts)
|
||
_append_cstr(out, outsz, ") -> ")
|
||
if string.strcmp(rmt, "none") == 0:
|
||
_append_cstr(out, outsz, "()")
|
||
else:
|
||
_append_cstr(out, outsz, rmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirAsm(p: str, resname: str, rtyb: str, out: str, outsz: sizet):
|
||
"""内联汇编: llvm.inline_asm [has_side_effects] [asm_dialect = intel] "…", "…" %a, %b : (i32, i32) -> i32
|
||
|
||
中间文本(LLVM 词法)有两条来源:
|
||
builders 的 build_inline_asm → `call <ret> asm sideeffect "…", "…"()`
|
||
TPV CallGenCBuiltin 注入文本 → `call <ret> asm sideeffect inteldialect "…", "…"(<ty> %v, …)`
|
||
MLIR(llvm 方言) 形态由 mlir-opt 22.1.8 实测确定(**不得凭记忆**):
|
||
* 方言关键字是 `asm_dialect = intel`:LLVM 的裸 `inteldialect` 报
|
||
"expected attribute value";`asm_dialect = inteldialect` 报
|
||
"expected string or keyword containing one of the following enum values
|
||
for attribute 'asm_dialect' [att, intel]"。
|
||
* 输入操作数是**逗号分隔的裸操作数、不带括号**:`"…", "=r,r"(%x) : (i32) -> i32`
|
||
报 "expected ':'"(那正是 LLVM 词法,须改写)。
|
||
* 无返回 ⇒ `: (<操作数类型…>) -> ()`;有返回 ⇒ `-> ty`。
|
||
* **多结果**内联汇编在 MLIR 里是**单结果 + 结构体类型**:TPV 形态
|
||
`%N = call {i32, i32} asm sideeffect "…", "=r,r"()` 渲染为
|
||
`%N = llvm.inline_asm has_side_effects "…", "=r,r" : () -> !llvm.struct<(i32, i32)>`
|
||
(mlir-opt / mlir-translate 22.1.8 实测 exit 0,降级得
|
||
`call { i32, i32 } asm sideeffect …`),随后由 `llvm.extractvalue`
|
||
逐字段抽取(见 _MlirExtractValue)。此处无需特殊分支:返回类型文本经
|
||
MlirTypeText(`{…}` → `!llvm.struct<(...)>`)即可,嵌套结构体亦同。
|
||
* 实测通过组合:有/无 has_side_effects × 有/无 asm_dialect = intel ×
|
||
0/1/2 个操作数 × 有/无返回值。
|
||
非法形态一律响亮报错:除 `sideeffect`/`inteldialect` 之外的未知记号、
|
||
缺约束串、非字符串字面量、实参不成对、有返回值却无结果名。
|
||
"""
|
||
p = _mlir_ws(p + 3)
|
||
side: int = 0
|
||
intel: int = 0
|
||
g: int = 0
|
||
while g < 8:
|
||
g += 1
|
||
if string.strncmp(p, "sideeffect", 10) == 0 and (p[10] == ' ' or p[10] == '\t'):
|
||
side = 1
|
||
p = _mlir_ws(p + 10)
|
||
continue
|
||
if string.strncmp(p, "inteldialect", 12) == 0 and (p[12] == ' ' or p[12] == '\t'):
|
||
intel = 1
|
||
p = _mlir_ws(p + 12)
|
||
continue
|
||
break
|
||
# 汇编文本可长达数 KB(TPV 的 ASM_LINE_BUF_SIZE=4096),用 4096 缓冲承载,
|
||
# 不落小缓冲以免截断;转义对(\22/\5C/\0A)原样透传——MLIR 词法同样接受。
|
||
asmb: str = _SC_Tmp
|
||
p = _mlir_read_value(p, asmb, 4096)
|
||
if asmb[0] != '"':
|
||
MlirFail("MLIR 渲染: inline_asm 缺少汇编文本(或含未知关键字)")
|
||
p = _mlir_ws(p)
|
||
if p[0] != ',':
|
||
MlirFail("MLIR 渲染: inline_asm 缺少约束串(形态须为 \"asm\", \"constraints\")")
|
||
p = _mlir_ws(p + 1)
|
||
conb: str = _SC_Args
|
||
p = _mlir_read_value(p, conb, 2048)
|
||
if conb[0] != '"':
|
||
MlirFail("MLIR 渲染: inline_asm 约束串必须是字符串字面量")
|
||
p = _mlir_ws(p)
|
||
if p[0] != '(':
|
||
MlirFail("MLIR 渲染: inline_asm 缺少实参列表 '(...)'")
|
||
p = p + 1
|
||
ops: str = _SC_ArgTs
|
||
ops[0] = '\0'
|
||
tys: str = _SC_IdxV
|
||
tys[0] = '\0'
|
||
nops: int = 0
|
||
g = 0
|
||
while p[0] != '\0':
|
||
g += 1
|
||
if g > 256:
|
||
MlirFail("MLIR 渲染: inline_asm 实参过多")
|
||
p = _mlir_ws(p)
|
||
if p[0] == ')':
|
||
break
|
||
atyb: str = _SC_Ty2
|
||
p = _mlir_read_type(p, atyb, 512)
|
||
p = _mlir_ws(p)
|
||
avb: str = _SC_V1
|
||
p = _mlir_read_value(p, avb, 512)
|
||
if nops > 0:
|
||
_append_cstr(ops, 2048, ", ")
|
||
_append_cstr(tys, 1024, ", ")
|
||
_MlirOperand(avb, atyb, ops, 2048)
|
||
amt: str = _SC_Mt
|
||
amt[0] = '\0'
|
||
MlirTypeText(amt, 512, atyb, _MLIR_Pool)
|
||
_append_cstr(tys, 1024, amt)
|
||
nops += 1
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = p + 1
|
||
if p[0] == ')':
|
||
p = p + 1
|
||
p = _mlir_ws(p)
|
||
if p[0] != '\0':
|
||
MlirFail("MLIR 渲染: inline_asm 实参列表之后出现多余记号")
|
||
rmt: str = _SC_Buf
|
||
rmt[0] = '\0'
|
||
MlirTypeText(rmt, 512, rtyb, _MLIR_Pool)
|
||
if string.strcmp(rmt, "none") == 0:
|
||
_MlirEmitRes(out, outsz, None)
|
||
else:
|
||
if resname is None:
|
||
MlirFail("MLIR 渲染: inline_asm 有返回值但缺少结果名(%N = call …)")
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.inline_asm")
|
||
if side != 0:
|
||
_append_cstr(out, outsz, " has_side_effects")
|
||
if intel != 0:
|
||
_append_cstr(out, outsz, " asm_dialect = intel")
|
||
_append_cstr(out, outsz, " ")
|
||
_append_cstr(out, outsz, asmb)
|
||
_append_cstr(out, outsz, ", ")
|
||
_append_cstr(out, outsz, conb)
|
||
if nops > 0:
|
||
_append_cstr(out, outsz, " ")
|
||
_append_cstr(out, outsz, ops)
|
||
_append_cstr(out, outsz, " : (")
|
||
_append_cstr(out, outsz, tys)
|
||
_append_cstr(out, outsz, ") -> ")
|
||
if string.strcmp(rmt, "none") == 0:
|
||
_append_cstr(out, outsz, "()")
|
||
else:
|
||
_append_cstr(out, outsz, rmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirCast(p: str, resname: str, ob: str, out: str, outsz: sizet):
|
||
"""转换指令: llvm.zext %v : i32 to i64"""
|
||
ftyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, ftyb, 512)
|
||
p = _mlir_ws(p)
|
||
vb: str = _SC_V1
|
||
p = _mlir_read_value(p, vb, 512)
|
||
p = _mlir_ws(p)
|
||
if string.strncmp(p, "to ", 3) == 0:
|
||
p = _mlir_ws(p + 3)
|
||
ttyb: str = _SC_Ty2
|
||
_mlir_read_type(p, ttyb, 512)
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.")
|
||
_append_cstr(out, outsz, ob)
|
||
_append_cstr(out, outsz, " ")
|
||
_MlirOperand(vb, ftyb, out, outsz)
|
||
fmt: str = _SC_Mt
|
||
fmt[0] = '\0'
|
||
MlirTypeText(fmt, 512, ftyb, _MLIR_Pool)
|
||
tmt: str = _SC_Tmp2
|
||
tmt[0] = '\0'
|
||
MlirTypeText(tmt, 1024, ttyb, _MLIR_Pool)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, fmt)
|
||
_append_cstr(out, outsz, " to ")
|
||
_append_cstr(out, outsz, tmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirBitcast(p: str, resname: str, out: str, outsz: sizet):
|
||
"""bitcast: ptr→ptr 视为 no-op(记录别名);否则 llvm.bitcast %v : T to U"""
|
||
ftyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, ftyb, 512)
|
||
p = _mlir_ws(p)
|
||
vb: str = _SC_V1
|
||
p = _mlir_read_value(p, vb, 512)
|
||
p = _mlir_ws(p)
|
||
if string.strncmp(p, "to ", 3) == 0:
|
||
p = _mlir_ws(p + 3)
|
||
ttyb: str = _SC_Ty2
|
||
_mlir_read_type(p, ttyb, 512)
|
||
if MlirTypeTextIsPointer(ftyb) != 0 and MlirTypeTextIsPointer(ttyb) != 0:
|
||
# MLIR 不透明指针下 ptr→ptr bitcast 无意义 → no-op:
|
||
# 把结果名登记为源操作数别名(后续引用自动改写),不输出指令
|
||
if resname is None:
|
||
MlirFail("MLIR 渲染: no-op bitcast 缺少结果名")
|
||
opnd: str = _MlirOperand(vb, ftyb, None, 0)
|
||
_MlirAliasAdd(resname, opnd)
|
||
return
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.bitcast ")
|
||
_MlirOperand(vb, ftyb, out, outsz)
|
||
fmt: str = _SC_Mt
|
||
fmt[0] = '\0'
|
||
MlirTypeText(fmt, 512, ftyb, _MLIR_Pool)
|
||
tmt: str = _SC_Tmp2
|
||
tmt[0] = '\0'
|
||
MlirTypeText(tmt, 1024, ttyb, _MLIR_Pool)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, fmt)
|
||
_append_cstr(out, outsz, " to ")
|
||
_append_cstr(out, outsz, tmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirSelect(p: str, resname: str, out: str, outsz: sizet):
|
||
"""select: llvm.select %c, %a, %b : i1, i32"""
|
||
ctyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, ctyb, 512)
|
||
p = _mlir_ws(p)
|
||
cb: str = _SC_V1
|
||
p = _mlir_read_value(p, cb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
ttyb: str = _SC_Ty2
|
||
p = _mlir_read_type(p, ttyb, 512)
|
||
p = _mlir_ws(p)
|
||
tb: str = _SC_V2
|
||
p = _mlir_read_value(p, tb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
ftyb: str = _SC_Buf
|
||
p = _mlir_read_type(p, ftyb, 512)
|
||
p = _mlir_ws(p)
|
||
fb: str = _SC_V3
|
||
_mlir_read_value(p, fb, 512)
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.select ")
|
||
_MlirOperand(cb, ctyb, out, outsz)
|
||
_append_cstr(out, outsz, ", ")
|
||
_MlirOperand(tb, ttyb, out, outsz)
|
||
_append_cstr(out, outsz, ", ")
|
||
_MlirOperand(fb, ttyb, out, outsz)
|
||
cmt: str = _SC_Mt
|
||
cmt[0] = '\0'
|
||
MlirTypeText(cmt, 512, ctyb, _MLIR_Pool)
|
||
vmt: str = _SC_Tmp2
|
||
vmt[0] = '\0'
|
||
MlirTypeText(vmt, 1024, ttyb, _MLIR_Pool)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, cmt)
|
||
_append_cstr(out, outsz, ", ")
|
||
_append_cstr(out, outsz, vmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirGep(p: str, resname: str, out: str, outsz: sizet):
|
||
"""getelementptr: llvm.getelementptr %p[i0, i1] : (!llvm.ptr, t0, t1) -> !llvm.ptr, base"""
|
||
baseb: str = _SC_Ty2
|
||
p = _mlir_read_type(p, baseb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
ptyb: str = _SC_Buf
|
||
p = _mlir_read_type(p, ptyb, 512)
|
||
p = _mlir_ws(p)
|
||
pb: str = _SC_V3
|
||
p = _mlir_read_value(p, pb, 512)
|
||
idxv: str = _SC_IdxV
|
||
idxt: str = _SC_IdxT
|
||
idxv[0] = '\0'
|
||
idxt[0] = '\0'
|
||
p = _mlir_ws(p)
|
||
nidx: int = 0
|
||
tmpv: str = _SC_Tmp
|
||
tmpv[0] = '\0'
|
||
g: int = 0
|
||
while p[0] == ',':
|
||
g += 1
|
||
if g > 32:
|
||
MlirFail("MLIR 渲染: GEP 索引过多")
|
||
p = _mlir_ws(p + 1)
|
||
ityb: str = _SC_Ty
|
||
p = _mlir_read_type(p, ityb, 512)
|
||
p = _mlir_ws(p)
|
||
ivb: str = _SC_V1
|
||
p = _mlir_read_value(p, ivb, 512)
|
||
if nidx > 0:
|
||
_append_cstr(idxv, 1024, ", ")
|
||
_append_cstr(idxt, 1024, ", ")
|
||
_MlirOperand(ivb, ityb, tmpv, 4096)
|
||
_append_cstr(idxv, 1024, tmpv)
|
||
tmpv[0] = '\0'
|
||
imt: str = _SC_Mt
|
||
imt[0] = '\0'
|
||
MlirTypeText(imt, 512, ityb, _MLIR_Pool)
|
||
_append_cstr(idxt, 1024, imt)
|
||
nidx += 1
|
||
p = _mlir_ws(p)
|
||
if nidx == 0:
|
||
MlirFail("MLIR 渲染: GEP 缺少索引")
|
||
bmt: str = _SC_Tmp2
|
||
bmt[0] = '\0'
|
||
MlirTypeText(bmt, 1024, baseb, _MLIR_Pool)
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.getelementptr ")
|
||
_MlirOperand(pb, ptyb, out, outsz)
|
||
_append_cstr(out, outsz, "[")
|
||
_append_cstr(out, outsz, idxv)
|
||
_append_cstr(out, outsz, "] : (!llvm.ptr")
|
||
# 每个索引一个类型
|
||
_MlirAppendCommaTypes(out, outsz, idxt)
|
||
_append_cstr(out, outsz, ") -> !llvm.ptr, ")
|
||
_append_cstr(out, outsz, bmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirAppendCommaTypes(out: str, outsz: sizet, tylist: str):
|
||
"""把 ", t0, t1" 追加到 out(tylist 是逗号分隔的类型串)"""
|
||
if tylist is None or tylist[0] == '\0':
|
||
return
|
||
_append_cstr(out, outsz, ", ")
|
||
_append_cstr(out, outsz, tylist)
|
||
|
||
|
||
def _MlirSwitch(p: str, out: str, outsz: sizet):
|
||
"""switch: llvm.switch %v : i32, ^default [0: ^case0, 1: ^case1]
|
||
|
||
形态由 mlir-opt 22.1.8 实测确定(设计规范里的
|
||
`llvm.switch %v, ^default [0: ^case0] : i32` **被拒绝**:报 "expected ':'"):
|
||
`llvm.switch %v : i32, ^default [0: ^c0, 1: ^c1]` → exit 0 ✓(采用)
|
||
`llvm.switch %v : i32, ^default(%r : i32) [ … ]` → exit 0 ✓(default 块实参)
|
||
`llvm.switch %v : i32, ^default [0: ^c0(%r : i32)]` → exit 0 ✓(case 块实参)
|
||
`llvm.switch %v : i64, ^default [4294967296: ^c0]` → exit 0 ✓(case 值随值类型)
|
||
`llvm.switch %v : i32, ^default [-1: ^c0]` → exit 0 ✓
|
||
`llvm.switch %v : i32, ^default []` → exit 0 ✓(空 case 表合法)
|
||
即**值类型紧跟值之后**(`%v : i32,`),块实参写法与 br/cond_br 完全同构,
|
||
故 `_MlirBlockOperands` 的 phi→块实参设施在此原样复用 ⇒ **支持目标块带 phi**
|
||
(匹配不到入边会响亮报错,绝不静默丢实参)。
|
||
case 值仍必须是常量:MLIR 里 case 值是属性(case_values),SSA 值不可用。
|
||
"""
|
||
tyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, tyb, 512)
|
||
p = _mlir_ws(p)
|
||
vb: str = _SC_V1
|
||
p = _mlir_read_value(p, vb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
if string.strncmp(p, "label ", 6) == 0:
|
||
p = _mlir_ws(p + 6)
|
||
db: str = _SC_V2
|
||
p = _mlir_read_value(p, db, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == '[':
|
||
p = p + 1
|
||
# 先物化值操作数与其 MLIR 类型文本:case 循环里的 _MlirBlockOperands 会复用
|
||
# _SC_Ty/_SC_Buf/_SC_P1/_SC_P2 等 scratch,捕获到独立缓冲后才安全。
|
||
vop: str = _SC_IdxT
|
||
vop[0] = '\0'
|
||
_MlirOperand(vb, tyb, vop, 1024)
|
||
vmt: str = _SC_IdxV
|
||
vmt[0] = '\0'
|
||
MlirTypeText(vmt, 1024, tyb, _MLIR_Pool)
|
||
cases: str = _SC_Tmp2
|
||
cases[0] = '\0'
|
||
ncase: int = 0
|
||
g: int = 0
|
||
while p[0] != '\0':
|
||
g += 1
|
||
if g > 1024:
|
||
MlirFail("MLIR 渲染: switch 分支过多")
|
||
p = _mlir_ws(p)
|
||
if p[0] == ']' or p[0] == '\0':
|
||
break
|
||
ctb: str = _SC_P1
|
||
p = _mlir_read_type(p, ctb, 512)
|
||
p = _mlir_ws(p)
|
||
cvb: str = _SC_P2
|
||
p = _mlir_read_value(p, cvb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
if string.strncmp(p, "label ", 6) == 0:
|
||
p = _mlir_ws(p + 6)
|
||
cb: str = _SC_Ty2
|
||
p = _mlir_read_value(p, cb, 512)
|
||
if cvb[0] == '%':
|
||
MlirFail("MLIR 渲染: switch case 值必须是常量(SSA 值不支持)")
|
||
cbr: str = _MlirBlkRef(cb)
|
||
if ncase > 0:
|
||
_append_cstr(cases, 1024, ", ")
|
||
_append_cstr(cases, 1024, cvb)
|
||
_append_cstr(cases, 1024, ": ^")
|
||
_append_cstr(cases, 1024, cbr)
|
||
# 目标块带 phi ⇒ 追加块实参(与 br/cond_br 同一设施)
|
||
_MlirBlockOperands(cases, 1024, _MlirFindBlock(_MLIR_Func, cbr), _MLIR_SrcBlk)
|
||
ncase += 1
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = p + 1
|
||
dr: str = _MlirBlkRef(db)
|
||
_MlirEmitRes(out, outsz, None)
|
||
_append_cstr(out, outsz, "llvm.switch ")
|
||
_append_cstr(out, outsz, vop)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, vmt)
|
||
_append_cstr(out, outsz, ", ^")
|
||
_append_cstr(out, outsz, dr)
|
||
_MlirBlockOperands(out, outsz, _MlirFindBlock(_MLIR_Func, dr), _MLIR_SrcBlk)
|
||
_append_cstr(out, outsz, " [")
|
||
_append_cstr(out, outsz, cases)
|
||
_append_cstr(out, outsz, "]\n")
|
||
|
||
|
||
def _MlirAtomicRmw(p: str, resname: str, out: str, outsz: sizet):
|
||
"""atomicrmw: llvm.atomicrmw add %p, %v monotonic : !llvm.ptr, i32"""
|
||
aop: str = _SC_P1
|
||
p = _mlir_read_value(p, aop, 512)
|
||
p = _mlir_ws(p)
|
||
ptyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, ptyb, 512)
|
||
p = _mlir_ws(p)
|
||
pb: str = _SC_V1
|
||
p = _mlir_read_value(p, pb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
vtyb: str = _SC_Ty2
|
||
p = _mlir_read_type(p, vtyb, 512)
|
||
p = _mlir_ws(p)
|
||
vb: str = _SC_V2
|
||
p = _mlir_read_value(p, vb, 512)
|
||
p = _mlir_ws(p)
|
||
ordb: str = _SC_P2
|
||
_mlir_read_value(p, ordb, 512)
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.atomicrmw ")
|
||
_append_cstr(out, outsz, aop)
|
||
_append_cstr(out, outsz, " ")
|
||
_MlirOperand(pb, ptyb, out, outsz)
|
||
_append_cstr(out, outsz, ", ")
|
||
_MlirOperand(vb, vtyb, out, outsz)
|
||
_append_cstr(out, outsz, " ")
|
||
_append_cstr(out, outsz, ordb)
|
||
pmt: str = _SC_Mt
|
||
pmt[0] = '\0'
|
||
MlirTypeText(pmt, 512, ptyb, _MLIR_Pool)
|
||
vmt: str = _SC_Tmp2
|
||
vmt[0] = '\0'
|
||
MlirTypeText(vmt, 1024, vtyb, _MLIR_Pool)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, pmt)
|
||
_append_cstr(out, outsz, ", ")
|
||
_append_cstr(out, outsz, vmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirCmpXchg(p: str, resname: str, resname2: str,
|
||
out: str, outsz: sizet):
|
||
"""cmpxchg: MLIR 单结果 struct{T, i1} + 两条 extractvalue 导出旧值/成功位
|
||
|
||
中间文本(LLVM 词法,两态:builder 单结果 / 双结果前缀)
|
||
%N = cmpxchg <ptr_ty> <ptr>, <cmp_ty> <cmp>, <new_ty> <new> <succ> <fail>
|
||
%a, %b = cmpxchg …
|
||
MLIR 形态(mlir-opt 22.1.8 实测 exit 0;cmpxchg 在 MLIR 里是**单结果**,
|
||
结果为 struct{T, i1},不是两个 SSA 值):
|
||
%N = llvm.cmpxchg %p, %c, %n acq_rel monotonic : !llvm.ptr, i32
|
||
%_N_v = llvm.extractvalue %N[0] : !llvm.struct<(i32, i1)> ; 旧值(比较值)
|
||
%_N_ok = llvm.extractvalue %N[1] : !llvm.struct<(i32, i1)> ; i1 成功位
|
||
两条 extractvalue 的结果名由 struct 结果名**确定性派生**
|
||
(_MlirCxResName,如 `%0` → `%_0_v`/`%_0_ok`)。
|
||
两个结果名绑定:`%N`(单结果)/ `%a`(双结果首名)→ 旧值 `[0]`;
|
||
双结果次名 `%b` → 成功位 `[1]`。绑定走 bitcast no-op 同一别名设施
|
||
(_MlirAliasAdd),后续引用由 _MlirResolve 自动改写为 extractvalue 结果名。
|
||
这样 builder 端 `build_cmpxchg` 返回的「T 类型 Value」语义(旧值)得以保持。
|
||
"""
|
||
ptyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, ptyb, 512)
|
||
p = _mlir_ws(p)
|
||
pb: str = _SC_V1
|
||
p = _mlir_read_value(p, pb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
ctyb: str = _SC_Ty2
|
||
p = _mlir_read_type(p, ctyb, 512)
|
||
p = _mlir_ws(p)
|
||
cb: str = _SC_V2
|
||
p = _mlir_read_value(p, cb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
ntyb: str = _SC_B1
|
||
p = _mlir_read_type(p, ntyb, 512)
|
||
p = _mlir_ws(p)
|
||
nb: str = _SC_B2
|
||
p = _mlir_read_value(p, nb, 512)
|
||
p = _mlir_ws(p)
|
||
succ: str = _SC_P1
|
||
p = _mlir_read_value(p, succ, 512)
|
||
p = _mlir_ws(p)
|
||
fail: str = _SC_P2
|
||
_mlir_read_value(p, fail, 512)
|
||
if resname is None:
|
||
MlirFail("MLIR 渲染: cmpxchg 缺少结果名")
|
||
# 两条 extractvalue 的结果名由 struct 结果名确定性派生(两遍必然同名)
|
||
onm: str = _MlirCxResName(resname, "_v")
|
||
snm: str = _MlirCxResName(resname, "_ok")
|
||
_MlirAliasAdd(resname, onm)
|
||
if resname2 is not None:
|
||
_MlirAliasAdd(resname2, snm)
|
||
# MLIR 类型文本(用非 _SC_Mt 缓冲:_MlirOperand 对字面量会占用 _SC_Mt)
|
||
pmt: str = _SC_Tmp2
|
||
pmt[0] = '\0'
|
||
MlirTypeText(pmt, 1024, ptyb, _MLIR_Pool)
|
||
vmt: str = _SC_IdxV
|
||
vmt[0] = '\0'
|
||
MlirTypeText(vmt, 1024, ctyb, _MLIR_Pool)
|
||
stmt: str = _SC_IdxT
|
||
stmt[0] = '\0'
|
||
_append_cstr(stmt, 1024, "!llvm.struct<(")
|
||
_append_cstr(stmt, 1024, vmt)
|
||
_append_cstr(stmt, 1024, ", i1)>")
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.cmpxchg ")
|
||
_MlirOperand(pb, ptyb, out, outsz)
|
||
_append_cstr(out, outsz, ", ")
|
||
_MlirOperand(cb, ctyb, out, outsz)
|
||
_append_cstr(out, outsz, ", ")
|
||
_MlirOperand(nb, ntyb, out, outsz)
|
||
_append_cstr(out, outsz, " ")
|
||
_append_cstr(out, outsz, succ)
|
||
_append_cstr(out, outsz, " ")
|
||
_append_cstr(out, outsz, fail)
|
||
_append_cstr(out, outsz, " : ")
|
||
_append_cstr(out, outsz, pmt)
|
||
_append_cstr(out, outsz, ", ")
|
||
_append_cstr(out, outsz, vmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
_MlirEmitRes(out, outsz, onm)
|
||
_append_cstr(out, outsz, "llvm.extractvalue ")
|
||
_append_cstr(out, outsz, resname)
|
||
_append_cstr(out, outsz, "[0] : ")
|
||
_append_cstr(out, outsz, stmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
_MlirEmitRes(out, outsz, snm)
|
||
_append_cstr(out, outsz, "llvm.extractvalue ")
|
||
_append_cstr(out, outsz, resname)
|
||
_append_cstr(out, outsz, "[1] : ")
|
||
_append_cstr(out, outsz, stmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirVaArg(p: str, resname: str, out: str, outsz: sizet):
|
||
"""va_arg: 用**通用 op 语法**(刻意选择,见下)
|
||
|
||
中间文本(LLVM 词法): %N = va_arg <va_list_ty> <va_list_ptr>, <arg_ty>
|
||
MLIR 文本: %N = "llvm.va_arg"(%ap) : (!llvm.ptr) -> i32
|
||
|
||
为什么必须用 generic 形态(mlir-opt 22.1.8 实测):自定义汇编语法
|
||
`llvm.va_arg %ap, i32` 要求显式给出 va_list 的类型(LLVM 里是 `ptr`),
|
||
而不透明指针下该类型无法表达 —— mlir-opt 报 "expected builtin.function",
|
||
故改用通用 op 语法(generic form),只用操作数类型列表描述。
|
||
降级验证(mlir-translate --mlir-to-llvmir)实测得到
|
||
%2 = va_arg ptr %0, i32
|
||
与 LLVM 的 va_arg 完全同形。
|
||
"""
|
||
vtyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, vtyb, 512)
|
||
p = _mlir_ws(p)
|
||
vb: str = _SC_V1
|
||
p = _mlir_read_value(p, vb, 512)
|
||
p = _mlir_ws(p)
|
||
if p[0] == ',':
|
||
p = _mlir_ws(p + 1)
|
||
atyb: str = _SC_Ty2
|
||
_mlir_read_type(p, atyb, 512)
|
||
if resname is None:
|
||
MlirFail("MLIR 渲染: va_arg 缺少结果名")
|
||
# 操作数类型 / 结果类型(_MlirOperand 只用 _SC_Mt,故此处先取独立缓冲)
|
||
omt: str = _SC_Tmp2
|
||
omt[0] = '\0'
|
||
MlirTypeText(omt, 1024, vtyb, _MLIR_Pool)
|
||
rmt: str = _SC_IdxV
|
||
rmt[0] = '\0'
|
||
MlirTypeText(rmt, 1024, atyb, _MLIR_Pool)
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "\"llvm.va_arg\"(")
|
||
_MlirOperand(vb, vtyb, out, outsz)
|
||
_append_cstr(out, outsz, ") : (")
|
||
_append_cstr(out, outsz, omt)
|
||
_append_cstr(out, outsz, ") -> ")
|
||
_append_cstr(out, outsz, rmt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
def _MlirExtractValue(p: str, resname: str, out: str, outsz: sizet):
|
||
"""extractvalue: llvm.extractvalue %v[i0, i1] : <聚合类型>
|
||
|
||
中间文本是 LLVM 语法(**聚合类型文本在值之前,索引在逗号之后**):
|
||
%r = extractvalue {i32, i32} %v, 0
|
||
%r = extractvalue {i32, {i32, i32}} %v, 1, 0 ; 多级索引
|
||
MLIR 形态(mlir-opt 22.1.8 实测 exit 0):
|
||
%r = llvm.extractvalue %v[0] : !llvm.struct<(i32, i32)>
|
||
%r = llvm.extractvalue %v[1, 0] : !llvm.struct<(i32, !llvm.struct<(i32, i32)>)>
|
||
索引列表在 MLIR 里写在 `[]` 内、以 `, ` 分隔(与 LLVM 的 `<聚合>, i0, i1`
|
||
词法不同,必须转写);聚合类型文本经 MlirTypeText 转成 MLIR 类型
|
||
(匿名 `{…}` → !llvm.struct<(...)>、具名 `%"name"` → 内联具名体、
|
||
数组 `[N x T]` → !llvm.array<N x T>,三者均已实测 exit 0)。
|
||
真实来源:TPV 的**多结果内联汇编**(`%asm_result = call {i32, i32} asm …`)
|
||
之后逐字段抽取(`%asm_out_0 = extractvalue {i32, i32} %asm_result, 0`,
|
||
语料实测均为单索引);多级索引同样经 mlir-opt 实测合法,故一并支持。
|
||
索引必须是常量字面量(LLVM 词法如此)——SSA 索引 ⇒ 响亮报错。
|
||
"""
|
||
atyb: str = _SC_Ty
|
||
p = _mlir_read_type(p, atyb, 512)
|
||
p = _mlir_ws(p)
|
||
vb: str = _SC_V1
|
||
p = _mlir_read_value(p, vb, 512)
|
||
idxs: str = _SC_IdxV
|
||
idxs[0] = '\0'
|
||
nidx: int = 0
|
||
g: int = 0
|
||
# 索引列表:LLVM 词法里逗号分隔(`<聚合>, i0, i1`),逐个读出后转写;
|
||
# 循环上限与文件内其它解析循环同款(有界,超出即响亮报错)。
|
||
while g < 64:
|
||
p = _mlir_ws(p)
|
||
if p[0] != ',':
|
||
break
|
||
g += 1
|
||
if g > 32:
|
||
MlirFail("MLIR 渲染: extractvalue 索引过多")
|
||
p = _mlir_ws(p + 1)
|
||
ib: str = _SC_IdxT
|
||
p = _mlir_read_value(p, ib, 512)
|
||
if ib[0] == '\0':
|
||
MlirFail("MLIR 渲染: extractvalue 缺少索引")
|
||
if ib[0] == '%':
|
||
MlirFail("MLIR 渲染: extractvalue 索引必须是常量(SSA 索引不支持)")
|
||
if nidx > 0:
|
||
_append_cstr(idxs, 1024, ", ")
|
||
_append_cstr(idxs, 1024, ib)
|
||
nidx += 1
|
||
if nidx == 0:
|
||
MlirFail("MLIR 渲染: extractvalue 缺少索引")
|
||
if p[0] != '\0':
|
||
MlirFail("MLIR 渲染: extractvalue 索引之后出现多余记号")
|
||
# MLIR 聚合类型文本先落独立缓冲(_MlirOperand 只占 _SC_Mt,不冲突)
|
||
amt: str = _SC_Tmp2
|
||
amt[0] = '\0'
|
||
MlirTypeText(amt, 1024, atyb, _MLIR_Pool)
|
||
_MlirEmitRes(out, outsz, resname)
|
||
_append_cstr(out, outsz, "llvm.extractvalue ")
|
||
_MlirOperand(vb, atyb, out, outsz)
|
||
_append_cstr(out, outsz, "[")
|
||
_append_cstr(out, outsz, idxs)
|
||
_append_cstr(out, outsz, "] : ")
|
||
_append_cstr(out, outsz, amt)
|
||
_append_cstr(out, outsz, "\n")
|
||
|
||
|
||
# ============================================================
|
||
# 指令行 → MLIR 分派
|
||
# ============================================================
|
||
def _MlirRenderLine(text: str, out: str, outsz: sizet):
|
||
"""解析一条中间指令文本并渲染为 MLIR(out=None 时仅登记/校验)"""
|
||
global _MLIR_Pass, _MLIR_CurLine
|
||
if text is None:
|
||
return
|
||
# 入口寄存"当前行":类型文本转换失败时随报文打印(两次 pass 都是同一行,
|
||
# pass1 失败也能定位到具体中间文本)。text 指向 Line.Buf,生命周期覆盖整次渲染。
|
||
_MLIR_CurLine = text
|
||
MlirSetContextLine(_MLIR_CurLine)
|
||
p: str = _mlir_ws(text)
|
||
if p[0] == '\0':
|
||
return
|
||
resname: str = None
|
||
resname2: str = None
|
||
if p[0] == '%':
|
||
tb: str = _SC_Tb
|
||
q: str = _mlir_read_value(p, tb, 512)
|
||
q2: str = _mlir_ws(q)
|
||
if q2[0] == '=':
|
||
resname = _mlir_dup(tb)
|
||
p = _mlir_ws(q2 + 1)
|
||
elif q2[0] == ',':
|
||
# 双结果前缀 `%a, %b = <op>`(仅 cmpxchg):MLIR 的 cmpxchg 是**单结果**
|
||
# struct{T, i1},两个名字分别绑定到两条 extractvalue(见 _MlirCmpXchg)。
|
||
resname = _mlir_dup(tb)
|
||
q3: str = _mlir_ws(q2 + 1)
|
||
tb2: str = _SC_V3
|
||
q3 = _mlir_read_value(q3, tb2, 512)
|
||
q4: str = _mlir_ws(q3)
|
||
if q4[0] != '=':
|
||
MlirFail("MLIR 渲染: 双结果前缀(%a, %b =)缺少 '='")
|
||
resname2 = _mlir_dup(tb2)
|
||
p = _mlir_ws(q4 + 1)
|
||
ob: str = _SC_Op
|
||
p = _mlir_read_value(p, ob, 64)
|
||
if ob[0] == '\0':
|
||
MlirFail("MLIR 渲染: 空指令")
|
||
if resname2 is not None and string.strcmp(ob, "cmpxchg") != 0:
|
||
MlirFail("MLIR 渲染: 双结果前缀(%a, %b =)只支持 cmpxchg")
|
||
if _mlir_in_set(ob, "|add|sub|mul|sdiv|udiv|srem|urem|and|or|xor|shl|lshr|ashr|") != 0:
|
||
_MlirBinop(p, resname, ob, out, outsz)
|
||
return
|
||
if _mlir_in_set(ob, "|fadd|fsub|fmul|fdiv|frem|") != 0:
|
||
_MlirBinop(p, resname, ob, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "fneg") == 0:
|
||
_MlirFneg(p, resname, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "icmp") == 0:
|
||
_MlirCmp(p, resname, ob, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "fcmp") == 0:
|
||
_MlirCmp(p, resname, ob, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "alloca") == 0:
|
||
_MlirAlloca(p, resname, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "load") == 0:
|
||
_MlirLoad(p, resname, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "store") == 0:
|
||
_MlirStore(p, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "br") == 0:
|
||
_MlirBr(p, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "ret") == 0:
|
||
_MlirRet(p, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "unreachable") == 0:
|
||
_MlirUnreachable(out, outsz)
|
||
return
|
||
if string.strcmp(ob, "call") == 0:
|
||
_MlirCall(p, resname, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "select") == 0:
|
||
_MlirSelect(p, resname, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "phi") == 0:
|
||
if out is not None:
|
||
# 块首 phi 已在 _MlirEmitBlockLines 中跳过;走到这里说明 phi 不在块首
|
||
MlirFail("MLIR 渲染: phi 不在块首(v1 只支持块首连续 phi → 块参数)")
|
||
_MlirPhiScan(text)
|
||
return
|
||
if string.strcmp(ob, "getelementptr") == 0:
|
||
_MlirGep(p, resname, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "bitcast") == 0:
|
||
_MlirBitcast(p, resname, out, outsz)
|
||
return
|
||
if _mlir_in_set(ob, "|sext|trunc|zext|ptrtoint|inttoptr|fpext|fptrunc|fptosi|sitofp|fptoui|uitofp|") != 0:
|
||
_MlirCast(p, resname, ob, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "switch") == 0:
|
||
_MlirSwitch(p, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "atomicrmw") == 0:
|
||
_MlirAtomicRmw(p, resname, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "cmpxchg") == 0:
|
||
_MlirCmpXchg(p, resname, resname2, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "va_arg") == 0:
|
||
_MlirVaArg(p, resname, out, outsz)
|
||
return
|
||
if string.strcmp(ob, "extractvalue") == 0:
|
||
# 真实来源:TPV 的多结果内联汇编结果抽取(见 _MlirExtractValue)。
|
||
# 注:`insertvalue` 等其它未支持 opcode 在语料中 0 命中,
|
||
# 落到下面的兜底响亮报错(保持"未知指令 opcode"即可)。
|
||
_MlirExtractValue(p, resname, out, outsz)
|
||
return
|
||
# 注:内联汇编在中间文本里 opcode 是 "call"(call <ret> asm …),
|
||
# 由 _MlirCall 内部识别并转交 _MlirAsm 渲染(llvm.inline_asm)。
|
||
MlirFail("MLIR 渲染: 未知指令 opcode")
|
||
|
||
|
||
# ============================================================
|
||
# 函数签名与函数体
|
||
# ============================================================
|
||
def _MlirFuncSig(buf: str, size: sizet,
|
||
func: rawptr[Function], pool: memhub.MemBuddy*):
|
||
"""llvm.func @name(%a: i32, %b: i32) -> i32(void 返回省略 -> ty)"""
|
||
_append_cstr(buf, size, "llvm.func @")
|
||
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, "(")
|
||
params: rawptr[GSList[Param]] = func.Params
|
||
cur: rawptr[Param] = None
|
||
if params is not None:
|
||
cur = params.Head
|
||
first: int = 1
|
||
cur_steps: int = 0
|
||
while cur is not None:
|
||
cur_steps += 1
|
||
if cur_steps > IR_DUMP_CHAIN_LIMIT:
|
||
MlirFail("MLIR 渲染: 参数链疑似成环")
|
||
if first == 0:
|
||
_append_cstr(buf, size, ", ")
|
||
if cur.Name is not None:
|
||
if cur.Name[0] != '%':
|
||
_append_cstr(buf, size, "%")
|
||
_append_cstr(buf, size, cur.Name)
|
||
_append_cstr(buf, size, ": ")
|
||
ty_buf: str = _SC_Ty
|
||
ty_buf[0] = '\0'
|
||
TypePrint(ty_buf, 512, cur.Ty, pool)
|
||
_append_cstr(buf, size, ty_buf)
|
||
cur = cur.Next
|
||
first = 0
|
||
# 变参函数:参数表尾追加 `...`(mlir-opt 22.1.8 实测 exit 0:
|
||
# `llvm.func @f(%p: !llvm.ptr, ...) -> i32` / 无固定参数时 `llvm.func @f(...) -> i32`)
|
||
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, ")")
|
||
# 返回类型(Void 省略)
|
||
is_void: int = 0
|
||
match func.RetTy:
|
||
case LLVMType.Void():
|
||
is_void = 1
|
||
case _:
|
||
is_void = 0
|
||
if is_void == 0:
|
||
ret_buf: str = _SC_Ty2
|
||
ret_buf[0] = '\0'
|
||
TypePrint(ret_buf, 512, func.RetTy, pool)
|
||
_append_cstr(buf, size, " -> ")
|
||
_append_cstr(buf, size, ret_buf)
|
||
|
||
|
||
def FunctionPrint(buf: str, size: sizet,
|
||
func: rawptr[Function], pool: memhub.MemBuddy*):
|
||
"""将函数 func 序列化为 MLIR(llvm 方言) 文本**追加**写入 buf
|
||
|
||
注意:与 llvmlite 版一致,本函数为追加写(供模块打印连续拼接多个函数),
|
||
调用方若要单函数文本需自行先置 buf[0]='\\0'。
|
||
"""
|
||
global _MLIR_Pass, _MLIR_Func, _MLIR_SrcBlk, _MLIR_CurLine
|
||
if func is None or buf is None or size == 0:
|
||
return
|
||
|
||
# 先本地窥孔(ret zeroinitializer→0/null、入口槽 store→load 转发、
|
||
# no-op bitcast 消除):窥孔按"名称一致性"匹配、删除行置 Buf=None。
|
||
# 窥孔作用在 **builders 生成的中间指令文本** 层,与 MLIR 渲染正交;
|
||
# RenumberSSA 内部会跳过 declare 函数(IsDeclared==1)。
|
||
if func.IsDeclared == 0 and G_PeepEnable != 0:
|
||
_peephole_opt(func, pool)
|
||
RenumberSSA(func, pool)
|
||
|
||
_MlirTablesInit(pool)
|
||
_MLIR_Func = func
|
||
# 清空上一函数的行上下文:签名/declare 等非"行驱动"位置若报类型错,
|
||
# 不该带上一个已经无关的旧行,避免诊断误导(行上下文只在 _MlirRenderLine 赋值)。
|
||
_MLIR_CurLine = None
|
||
MlirSetContextLine(None)
|
||
|
||
if func.IsDeclared == 1:
|
||
# declare(无函数体)
|
||
_MlirFuncSig(buf, size, func, pool)
|
||
_append_cstr(buf, size, "\n")
|
||
return
|
||
|
||
blocks: rawptr[GSList[BasicBlock]] = func.Blocks
|
||
if blocks is None:
|
||
_MlirFuncSig(buf, size, func, pool)
|
||
_append_cstr(buf, size, "\n")
|
||
return
|
||
|
||
# ---- pass1:扫描(登记常量/别名 + 形态校验)----
|
||
_MLIR_Pass = 1
|
||
blk: rawptr[BasicBlock] = blocks.Head
|
||
blk_steps: int = 0
|
||
while blk is not None:
|
||
blk_steps += 1
|
||
if blk_steps > IR_DUMP_CHAIN_LIMIT:
|
||
MlirFail("MLIR 渲染: 函数块链疑似成环")
|
||
_MLIR_SrcBlk = blk
|
||
if blk.Lines is not None:
|
||
lines: rawptr[GSList[Line]] = blk.Lines
|
||
ln: rawptr[Line] = lines.Head
|
||
ln_steps: int = 0
|
||
while ln is not None:
|
||
ln_steps += 1
|
||
if ln_steps > IR_DUMP_CHAIN_LIMIT:
|
||
MlirFail("MLIR 渲染: 块指令链疑似成环")
|
||
if ln.Buf is not None:
|
||
_MlirRenderLine(ln.Buf, None, 0)
|
||
ln = ln.Next
|
||
blk = blk.Next
|
||
|
||
# ---- pass2:发射 ----
|
||
_MLIR_Pass = 2
|
||
_MlirFuncSig(buf, size, func, pool)
|
||
_append_cstr(buf, size, " {\n")
|
||
blk = blocks.Head
|
||
first: int = 1
|
||
blk_steps = 0
|
||
while blk is not None:
|
||
blk_steps += 1
|
||
if blk_steps > IR_DUMP_CHAIN_LIMIT:
|
||
MlirFail("MLIR 渲染: 函数块链疑似成环")
|
||
_MLIR_SrcBlk = blk
|
||
if first != 0:
|
||
# entry 块不带标签;常量物化 op 统一插在 entry 块开头
|
||
# (entry 支配全函数 → 常量在所有块内可见,且不破坏 phi 的块首约束)
|
||
_MlirCheckEntryNoPhi(blk)
|
||
_MlirEmitConsts(buf, size)
|
||
else:
|
||
_MlirEmitBlockHeader(buf, size, blk)
|
||
_MlirEmitBlockLines(buf, size, blk)
|
||
blk = blk.Next
|
||
first = 0
|
||
_append_cstr(buf, size, "}\n")
|
||
|
||
|
||
# ============================================================
|
||
# 内部辅助
|
||
# ============================================================
|
||
# _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: sizet = 0
|
||
|
||
def _append_cstr(dst: str, dst_size: sizet, src: str):
|
||
"""将 C 字符串 src 追加到 dst 末尾"""
|
||
global _g_last_dst, _g_last_pos
|
||
if dst is None or src is None: return
|
||
dlen: sizet = 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: sizet = string.strlen(src)
|
||
remain: sizet = dst_size - dlen
|
||
if remain <= 0: return
|
||
i: sizet = 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]) -> sizet:
|
||
"""获取函数名长度(创建时一次算好,恒等于 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
|