Files
Viper_VRTE/mlirlite/__function.vp
T
TermiNexus 7b006c96a0 feat(mlirlite): 补齐 Debug Info 同形 API,导出名集与 llvmlite 一致(215=215)
- __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
2026-10-03 07:43:03 +08:00

3417 lines
135 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import string
import viperlib
import memhub
import stdio
import stdlib
from linkedlist import GSListNode, GSList
from .__types import (
LLVMType, ParamNode, TypePrint,
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