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 zeroinitializer → ret 0;ret 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 中独立记号 % 替换为 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 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 %x to ,两侧类型相同)。 生成端(虚调用/联合位宽转换)会产生同类型恒等 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. %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 <谓词> <类型> , `——**谓词在类型之前**, 故读取顺序必须是 谓词 → 类型 → 操作数。 注意不能先读类型:那样会把谓词(如 `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: # 条件跳转:`<类型> , 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 (…) @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 asm [sideeffect] # [inteldialect] "", ""( %v, …)`(LLVM 词法), # MLIR 形态由 _MlirAsm 转写。 _MlirAsm(p, resname, rtyb, out, outsz) return if p[0] == '(': # 变参调用:中间文本是 `call (<实参类型…>, ...) @f(<实参>)` # (builder 的 build_call is_variadic=1 形态)。 # MLIR 形态(mlir-opt 22.1.8 实测 exit 0): # %0 = llvm.call @printf(%p, %n) vararg(!llvm.func) # : (!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 asm sideeffect "…", "…"()` TPV CallGenCBuiltin 注入文本 → `call asm sideeffect inteldialect "…", "…"( %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 , , %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 , 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,三者均已实测 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 = `(仅 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 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