Files
Viper_VRTE/mlirlite/__module.vp
T
TermiNexus 27121f56a7 refactor(includes): R12 下标 while 循环改 for range,R13 c.Set 迁移为赋值
R12(wiki/新规范 L27-39):把「i: int = 0 与 while i < N: … i += 1」形式的下标循环改写为「for i in range(N):」。保守判定:仅当条件为裸 NAME < EXPR、循环体无 continue、NAME 在体内无其它赋值、无嵌套重绑定、循环后同层不再引用 NAME 时才改写。共 269 处 / 48 文件。

R13:c.Set 是正在退化的路径——c.Set(*p, v) 期望写作 *p = v,c.Set(a, b) 等于 a = b。本次把 includes 内 45 处 c.Set 全部迁移为赋值,includes 已无 c.Set 调用。

验证:bootstrap 三重重编 SUCCESS(无 vast_c 解析回退);全量 77/77 顺序回归通过;ZlibTest 内部断言 79 passed / 0 failed。
2026-10-05 15:33:52 +08:00

1726 lines
75 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 stdio
import stdlib
import viperlib
import memhub
from linkedlist import GSList, GSListNode
from .__types import (
LLVMType, TypePrint, Array, PrintStructDefinition, get_struct_name, Struct,
ParamNode, MlirFail, MlirTypeText, MlirSetContextLine,
MlirNamedTypesBegin, MlirNamedTypesClear, MlirNamedTypeRegister,
paramnode_get_ty, paramnode_get_next,
)
from .__function import (
Function, FunctionPrint, _ll_name_needs_quote,
_mlir_read_type, _mlir_read_value, _mlir_ws,
_MlirSymRefAppend,
)
# ============================================================
# GlobalVariable: LLVM IR 全局变量
#
# 持有名称 + 类型 + 初始化值 + 常量标志 + 链接类型。
# 继承 GSListNode[GlobalVariable] 获取强类型 Next。
# ============================================================
novirtual class GlobalVariable(GSListNode[GlobalVariable]):
Name: str # 全局变量名(如 ".str" / "my_global")
Ty: rawptr[LLVMType] # 变量类型
Initializer: rawptr # 初始化值 Value*(None = 无初始化)
IsConstant: int # 1=constant, 0=variable
Linkage: str # 链接类型("private"/"internal"/"external"/...)
Section: str # 段名(None = 默认)
UnnamedAddr: int # 1=unnamed_addr, 2=local_unnamed_addr, 0=none
Align: int # 对齐字节数(0=默认对齐,如 @c.Attribute(t.attr.aligned(16)))
# ============================================================
# NamedTypeNode: 命名结构体类型链表节点
#
# 持有 LLVMType* 指针(必须是命名 Struct 类型)。
# 继承 GSListNode[NamedTypeNode] 获取强类型 Next。
# ============================================================
@t.NoVTable
class NamedTypeNode(GSListNode[NamedTypeNode]):
Ty: rawptr[LLVMType] # 命名结构体类型指针
IsPacked: int # 1=packed 结构体(MLIR: !llvm.struct<"name", packed (…)>,无字段填充)
# ============================================================
# DINode: DI metadata node(同形保留;MLIR 打印阶段忽略)
#
# 与 includes/llvmlite 逐名同形:Debug Info 在 MLIR 后端无对应概念,故本库
# 仅记录模型(供 TransPyV 的 DebugInfoGen 在 llvmlite.→mlirlite. 改名后仍可
# 编译),LLVMModulePrint **不输出**这些节点(输出会破坏 MLIR 语法)。
# ============================================================
@t.NoVTable
class DINode(GSListNode[DINode]):
Text: str # node body, e.g. "!DIFile(filename: \"a.vp\", directory: \"/x\")"
# ============================================================
# DINamedMeta: named metadata(同形保留;MLIR 打印阶段忽略)
# ============================================================
@t.NoVTable
class DINamedMeta(GSListNode[DINamedMeta]):
Name: str # e.g. "llvm.dbg.cu" / "llvm.module.flags"
Refs: str # reference list text, e.g. "!1"
# ============================================================
# LLVMModule: LLVM IR 模块容器
#
# 使用嵌入式 Head/Tail/Count 字段代替 GSList 指针,
# 避免泛型 GSList 的 __sizeof__ 返回 0 和 vtable 偏移不一致问题。
# LLVMModulePrint 输出完整 .ll 文件内容。
# 注意: 类名必须为 LLVMModule 而非 Module,避免与 ast.Module 命名冲突
# ============================================================
@t.NoVTable
class LLVMModule:
Name: str # 模块名
FuncHead: rawptr[Function] # 函数链表头
FuncTail: rawptr[Function] # 函数链表尾
FuncCount: sizet # 函数计数
GlobalHead: rawptr[GlobalVariable] # 全局变量链表头
GlobalTail: rawptr[GlobalVariable] # 全局变量链表尾
GlobalCount: sizet # 全局变量计数
TargetTriple: str # 目标三元组(如 "x86_64-pc-linux-gnu")
DataLayout: str # 数据布局(如 "e-m:e-p270:32:32-...")
NamedTypeHead: rawptr[NamedTypeNode] # 命名结构体类型链表头
NamedTypeTail: rawptr[NamedTypeNode] # 命名结构体类型链表尾
NamedTypeCount: sizet # 命名结构体类型计数
DIMetaHead: rawptr[DINode] # DI metadata node list head(同形保留;MLIR 不打印)
DIMetaTail: rawptr[DINode] # DI metadata node list tail
DIMetaCount: sizet # DI node count (= next !N id)
DINamedHead: rawptr[DINamedMeta] # named metadata list head
DINamedTail: rawptr[DINamedMeta] # named metadata list tail
DINamedCount: sizet # named metadata count
# 总计: 18 字段 * 8 = 144 字节(无 vtable;与 includes/llvmlite 同布局)
# ============================================================
# 实例方法(对象化重构:原 module_* 顶层函数改写为方法,
# 原顶层函数保留为一行委托)
# ============================================================
# ---- 链表操作 ----
def add_function(self, func: rawptr[Function]):
"""将函数追加到模块函数链表(O(1))"""
if self is None or func is None: return
func.Next = None
if self.FuncHead is None:
self.FuncHead = func
else:
self.FuncTail.Next = func
self.FuncTail = func
self.FuncCount += 1
def add_global(self, gv: rawptr[GlobalVariable]):
"""将全局变量追加到模块全局变量链表(O(1))"""
if self is None or gv is None: return
gv.Next = None
if self.GlobalHead is None:
self.GlobalHead = gv
else:
self.GlobalTail.Next = gv
self.GlobalTail = gv
self.GlobalCount += 1
# ---- 目标属性 ----
def set_target(self, triple: str):
"""设置目标三元组"""
if self is None: return
self.TargetTriple = triple
def set_datalayout(self, layout: str):
"""设置数据布局"""
if self is None: return
self.DataLayout = layout
# ---- DI debug metadata(同形保留;MLIR 打印阶段忽略,仅记录模型)----
def add_di_node(self, pool: memhub.MemBuddy*, text: str) -> int:
"""Append a DI metadata node; return its !N id (0-based, unique), -1 on failure.
text is the node body incl. !M references built from earlier ids.
MLIR 后端无 DI 概念:节点仅登记在模型里(LLVMModulePrint 不输出),
从而 TransPyV 的 DebugInfoGen 在改名后仍可编译、且不影响 MLIR 输出。
"""
if self is None or pool is None or text is None:
return -1
node: rawptr[DINode] = pool.alloc(DI_NODE_SIZE)
if node is None:
return -1
string.memset(node, 0, DI_NODE_SIZE)
# copy body (caller buffer may be reused)
tlen: sizet = string.strlen(text)
tcopy: str = pool.alloc(tlen + 1)
if tcopy is None:
return -1
string.strcpy(tcopy, text)
node.Text = tcopy
if self.DIMetaHead is None:
self.DIMetaHead = node
else:
self.DIMetaTail.Next = node
self.DIMetaTail = node
node_id: int = int(self.DIMetaCount)
self.DIMetaCount += 1
return node_id
def add_named_metadata(self, pool: memhub.MemBuddy*, name: str, refs: str):
"""Register named metadata "!<name> = !{ <refs> }"; merge into an existing
entry with the same name (refs is the brace body, e.g. "!3").
MLIR 侧仅记录模型,不产生输出(LLVMModulePrint 不输出 DI)。
"""
if self is None or pool is None or name is None:
return
cur: rawptr[DINamedMeta] = self.DINamedHead
cur_steps: sizet = 0
while cur is not None:
cur_steps += 1
if cur_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module di named(DINamedCount=%zu,dump 终止)\n",
self.DINamedCount)
stdlib.exit(1)
if cur.Name is not None and string.strcmp(cur.Name, name) == 0:
if refs is not None:
old_len: sizet = 0
if cur.Refs is not None:
old_len = string.strlen(cur.Refs)
merged: str = pool.alloc(old_len + string.strlen(refs) + 3)
if merged is not None:
merged[0] = '\0'
if cur.Refs is not None:
string.strcat(merged, cur.Refs)
string.strcat(merged, ", ")
string.strcat(merged, refs)
cur.Refs = merged
return
cur = cur.Next
entry: rawptr[DINamedMeta] = pool.alloc(DI_NAMED_SIZE)
if entry is None:
return
string.memset(entry, 0, DI_NAMED_SIZE)
ncopy: str = pool.alloc(string.strlen(name) + 1)
if ncopy is None:
return
string.strcpy(ncopy, name)
entry.Name = ncopy
if refs is not None:
rcopy: str = pool.alloc(string.strlen(refs) + 1)
if rcopy is None:
return
string.strcpy(rcopy, refs)
entry.Refs = rcopy
if self.DINamedHead is None:
self.DINamedHead = entry
else:
self.DINamedTail.Next = entry
self.DINamedTail = entry
self.DINamedCount += 1
# ---- 命名结构体类型管理 ----
def add_named_type(self, pool: memhub.MemBuddy*, ty: rawptr[LLVMType]):
"""登记命名结构体类型到模块(MLIR 侧仅保留模型,不产生输出)
非命名结构体(Name 为 None 或非 Struct 类型)会被忽略。
按 Name 去重;同名时 opaque 声明可被完整定义替换(供跨模块值类型 alloca)。
MLIR 无独立类型定义语法:结构体类型在使用处内联为 !llvm.struct<(…)>,
故本方法不再产生输出文本,命名类型链表(NamedTypeHead)仅作为模型保留,
供 module_set_named_type_packed / ensure_opaque_for_type 等原有 API 使用。
"""
if self is None or pool is None or ty is None:
return
name: str = get_struct_name(ty)
if name is None:
return
# 去重检查(opaque 声明可被完整定义替换)
cur: rawptr[NamedTypeNode] = self.NamedTypeHead
cnt_dedup: int = 0
while cur is not None:
# 步数守卫:NamedTypeHead 链表成环(内存损坏/插入 bug)会导致
# 无限遍历挂起(G3 编译 LinkedNode.prepend 挂起根因)。
# 正常链表节点数远小于此上限,超限即链表损坏 → 立即报错终止。
cnt_dedup += 1
if cnt_dedup > 100000:
stdio.printf("[ANT-LOOP] NamedTypeHead 链表疑似成环, name=%s, 遍历节点=%d, 强制终止\n",
name if name is not None else "(null)", cnt_dedup)
stdlib.exit(1)
if cur.Ty is not None:
existing_name: str = get_struct_name(cur.Ty)
if existing_name is not None:
if string.strcmp(existing_name, name) == 0:
# 已有完整定义则无需替换
if _is_opaque_struct(cur.Ty) == 0:
return
# 已有 opaque,新类型也是 opaque 则不重复
if _is_opaque_struct(ty) != 0:
return
# 用完整定义替换 opaque 声明(原地替换 Ty 指针)
cur.Ty = ty
return
cur = cur.Next
# 创建新节点
node: rawptr[NamedTypeNode] = pool.alloc(NAMED_TYPE_NODE_SIZE)
if node is None:
return
string.memset(node, 0, NAMED_TYPE_NODE_SIZE)
node.Ty = ty
node.IsPacked = 0
# opaque 类型插入链表头部,确保在引用它的完整定义之前声明。
# 例如 TypeRegistry{HashTable*, ...} 中,HashTable 的 opaque 声明
# 必须在 TypeRegistry 定义之前,否则 llc 报 "use of undefined type"。
# 完整定义追加到链表尾部,保持原有顺序。
if _is_opaque_struct(ty) != 0:
node.Next = self.NamedTypeHead
if self.NamedTypeHead is None:
self.NamedTypeTail = node
self.NamedTypeHead = node
else:
node.Next = None
if self.NamedTypeHead is None:
self.NamedTypeHead = node
else:
self.NamedTypeTail.Next = node
self.NamedTypeTail = node
self.NamedTypeCount += 1
def _is_type_defined_in_module(self, name: str) -> int:
"""检查模块中是否已定义指定名称的结构体类型,返回 1=已定义 / 0=未定义"""
if self is None or name is None:
return 0
cur: rawptr[NamedTypeNode] = self.NamedTypeHead
cnt_tdm: int = 0
while cur is not None:
# 步数守卫:同上,防 NamedTypeHead 链表成环无限遍历
cnt_tdm += 1
if cnt_tdm > 100000:
stdio.printf("[TDM-LOOP] NamedTypeHead 链表疑似成环, name=%s, 强制终止\n",
name if name is not None else "(null)")
stdlib.exit(1)
if cur.Ty is not None:
existing_name: str = get_struct_name(cur.Ty)
if existing_name is not None:
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp。
if existing_name[0] == name[0] and string.strcmp(existing_name, name) == 0:
return 1
cur = cur.Next
return 0
def ensure_opaque_for_type(self, pool: memhub.MemBuddy*,
ty: rawptr[LLVMType]):
"""为类型中的跨模块结构体引用添加 opaque 声明
当类型引用了 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时,
如果该名称未在模块中定义,则添加 opaque 声明。
"""
_scan_type_for_cross_module_refs(pool, self, ty)
# ---- 打印 ----
def PrintModule(self, buf: str, size: sizet,
pool: memhub.MemBuddy*, mode: int):
"""将模块序列化为 MLIR(llvm 方言) 文本写入 buf
输出骨架: module { <全局> <函数> }
MLIR 没有 stub/text 二分,用注释标明模式映射:
OUTPUT_FULL → 全量;OUTPUT_STUB → 仅全局/函数声明;OUTPUT_TEXT → 仅带体函数。
target triple / datalayout 不属于 MLIR 模块,记录但不输出(见注释与 README)。
"""
if self is None or buf is None or size == 0: return
# 清空行上下文:全局段不是"行驱动"渲染,报类型错时不该带上一函数残留的旧行
# (行上下文由 __function.vp 的 _MlirRenderLine 赋值,见 __types.vp 的 _MlirCtxLine)。
MlirSetContextLine(None)
# 发布本模块的命名结构体登记表:中间文本里"只有名字"的类型(`%"name"`)
# 经它回查完整定义、展开为使用处的内联具名形态(!llvm.struct<"name", (…)>)。
# 表随本次打印重建,打印结束(本函数末尾)即清空 —— 表内类型指针只在本次
# 打印期间有效,清空后不再解引用(避免 pool reset 后悬空)。
_MlirPublishNamedTypes(self, pool)
# 使 region 渲染草稿失效:本次打印首次用到时按当前 pool 惰性重建(见
# _MlirGvEnsureScratch)。跨打印绝不复用(上一次的池可能已释放/被复用)。
MlirGvResetScratch()
_append_cstr(buf, size, "module {\n")
# 模式说明注释(MLIR 无 stub/text 二分)
if mode == OUTPUT_STUB:
_append_cstr(buf, size, " // MLIR-STUB: 仅全局声明与函数声明\n")
elif mode == OUTPUT_TEXT:
_append_cstr(buf, size, " // MLIR-TEXT: 仅带体函数与含初值全局\n")
# target triple / datalayout:MLIR 模块不携带,由 mlir-translate/llc 阶段提供
if self.TargetTriple is not None or self.DataLayout is not None:
_append_cstr(buf, size,
" // target triple / datalayout 由 mlir-translate / llc 阶段提供\n")
# 行缩进辅助缓冲(模块成员统一缩进 2 空格)。
# 单函数/单全局的 MLIR 文本因内联具名结构体完整字段体而显著变长
# (旧 64 KiB 会截断;截断现由 FunctionPrint/LLVMModulePrint 的截断检查
# 响亮报错)。
# 容量上限**受模块池的 buddy 上限约束**:模块池是 ~2 MiB 子池
# (SUB_POOL_SIZE 2097136),其 max_order=15 ⇒ 单个 buddy 块最大
# 32<<15 = 1 MiB;分配还要留 16 字节块头 ⇒ 单次 alloc 请求必须
# ≤ 1 MiB − 16 = 1048560。
# 取该上限(1048560):此前取 1048576 会被判需 order 16 而**结构性失败**
# ([MEM] alloc 失败 size=1048576 needed=1048592 order=16 max_order=15);
# 而取 256 KiB 又对个别大函数偏小(触发"输出文本超出缓冲被截断"护栏)。
# 单函数文本因内联具名结构体完整字段体而显著变长,故按池可服务的最大单块给足;
# 若仍超出,截断由 FunctionPrint/LLVMModulePrint 的截断检查响亮报错。
tmp_cap: sizet = 1048560
tmp: str = pool.alloc(tmp_cap)
if tmp is None:
MlirFail("MLIR 渲染: 模块缩进辅助缓冲分配失败(1048560 ≈ 1 MiB − 16 字节块头)")
# 全局变量(MLIR 无独立类型定义行:命名结构体类型在使用处内联,
# 故这里不再输出 NamedTypeHead 类型定义,仅登记供类型文本回查)
gv: rawptr[GlobalVariable] = self.GlobalHead
gv_steps: sizet = 0
while gv is not None:
gv_steps += 1
if gv_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module globals(GlobalCount=%zu,dump 终止)\n",
self.GlobalCount)
stdlib.exit(1)
if mode == OUTPUT_TEXT and gv.Initializer is None:
# text 模式跳过无初值的全局变量(纯声明)
gv = gv.Next
continue
if tmp is not None:
tmp[0] = '\0'
if mode == OUTPUT_STUB:
_MlirGlobalLine(tmp, tmp_cap, pool, gv, 1)
else:
_MlirGlobalLine(tmp, tmp_cap, pool, gv, 0)
_MlirAppendIndented(buf, size, tmp, 2, pool)
gv = gv.Next
# 遍历函数(按模式过滤 declare/define)
func: rawptr[Function] = self.FuncHead
func_steps: sizet = 0
while func is not None:
func_steps += 1
if func_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module funcs print(FuncCount=%zu,dump 终止)\n",
self.FuncCount)
stdlib.exit(1)
emit: int = 0
if mode == OUTPUT_STUB:
if func.IsDeclared == 1:
emit = 1
elif mode == OUTPUT_TEXT:
if func.IsDeclared == 0:
emit = 1
else:
emit = 1
if emit != 0 and tmp is not None:
tmp[0] = '\0'
FunctionPrint(tmp, tmp_cap, func, pool)
_MlirAppendIndented(buf, size, tmp, 2, pool)
func = func.Next
_append_cstr(buf, size, "}\n")
# 打印结束:清空命名结构体登记表(表内类型指针此后不再解引用)
MlirNamedTypesClear()
# ============================================================
# _MlirPublishNamedTypes - 把模块的命名结构体链表发布给 __types.vp 的登记表
#
# 用途:类型文本里"只有名字"的形态(中间文本 `%"sha1.Cls"`)在 MLIR 侧必须
# 展开成使用处的内联具名体(MLIR 无定义行、且不允许无体前向声明),故需要一个
# 名字 → 完整定义的查表设施。NamedTypeHead 由 module_add_named_type 维护
# (同名至多一项,opaque 会被后到的完整定义就地替换),这里在模块打印开始时
# 逐项发布;opaque 项也发布,使引用它时报"仍为 opaque"(而不是"未登记")。
# 同时把节点的 packed 标记(NamedTypeNode.IsPacked,设置点唯一:
# module_set_named_type_packed)与类型一并登记,供渲染侧按名字回查。
# ============================================================
def _MlirPublishNamedTypes(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*):
"""按 NamedTypeHead 重建命名结构体登记表(打印期查表用,含 packed 标记)
先数一遍真实链表长度(不依赖 NamedTypeCount 字段),再按该数量精确分配槽位、
逐项登记 —— TPV 真实模块的具名类型可达数千个(见 README §5.9),故不做固定
容量假设;无具名类型时不分配。
"""
if mod is None or pool is None:
return
cnt: sizet = 0
nt: rawptr[NamedTypeNode] = mod.NamedTypeHead
nt_steps: sizet = 0
while nt is not None:
nt_steps += 1
if nt_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module named types count(NamedTypeCount=%zu,计数终止)\n",
mod.NamedTypeCount)
stdlib.exit(1)
cnt += 1
nt = nt.Next
MlirNamedTypesBegin(pool, cnt)
nt = mod.NamedTypeHead
nt_steps = 0
while nt is not None:
nt_steps += 1
if nt_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module named types publish(NamedTypeCount=%zu,发布终止)\n",
mod.NamedTypeCount)
stdlib.exit(1)
if nt.Ty is not None:
# packed 标记只挂在 NamedTypeNode 上(LLVMType.Struct 无该字段),
# 设置点唯一:module_set_named_type_packed。发布时与类型一并登记。
MlirNamedTypeRegister(nt.Ty, nt.IsPacked)
nt = nt.Next
# ============================================================
# 工厂函数
# ============================================================
def new_module(pool: memhub.MemBuddy*, name: str) -> rawptr[LLVMModule]:
"""创建一个模块"""
# 硬编码 144 字节:18 字段 * 8 字节(无 vtable,@t.NoVTable)
# 含 DI 字段(DIMetaHead/Tail/Count、DINamedHead/Tail/Count),与 llvmlite 同布局。
ptr: rawptr[LLVMModule] = pool.alloc(144)
if ptr is None: return None
string.memset(ptr, 0, 144)
ptr.Name = name
ptr.FuncHead = None
ptr.FuncTail = None
ptr.FuncCount = 0
ptr.GlobalHead = None
ptr.GlobalTail = None
ptr.GlobalCount = 0
ptr.TargetTriple = None
ptr.DataLayout = None
ptr.NamedTypeHead = None
ptr.NamedTypeTail = None
ptr.NamedTypeCount = 0
ptr.DIMetaHead = None
ptr.DIMetaTail = None
ptr.DIMetaCount = 0
ptr.DINamedHead = None
ptr.DINamedTail = None
ptr.DINamedCount = 0
return ptr
# GlobalVariable 硬编码大小: Next(8) + Name(8) + Ty(8) + Initializer(8) + IsConstant(4) + Linkage(8) + Section(8) + UnnamedAddr(4) + Align(4) = 72 字节
define GV_SIZE = 72
# NamedTypeNode 硬编码大小: Next(8) + Ty(8) + IsPacked(8) = 24 字节
define NAMED_TYPE_NODE_SIZE = 24
# DINode 硬编码大小: Next(8) + Text(8) = 16 字节(同 llvmlite)
define DI_NODE_SIZE = 16
# DINamedMeta 硬编码大小: Next(8) + Name(8) + Refs(8) = 24 字节(同 llvmlite)
define DI_NAMED_SIZE = 24
def new_global_variable(pool: memhub.MemBuddy*,
name: str, ty: rawptr[LLVMType]) -> rawptr[GlobalVariable]:
"""创建一个全局变量"""
ptr: rawptr[GlobalVariable] = pool.alloc(GV_SIZE)
if ptr is None: return None
string.memset(ptr, 0, GV_SIZE)
ptr.Name = name
ptr.Ty = ty
ptr.Initializer = None
ptr.IsConstant = 0
ptr.Linkage = "external"
ptr.Section = None
ptr.UnnamedAddr = 0
ptr.Align = 0
return ptr
# ============================================================
# 链表操作(嵌入式 Head/Tail/Count,O(1) 追加)
# ============================================================
def module_add_function(mod: rawptr[LLVMModule], func: rawptr[Function]):
mod.add_function(func)
def module_get_func_head(mod: rawptr[LLVMModule]) -> rawptr[Function]:
"""获取模块函数链表头(跨模块访问器,绕 stub 字段访问陷阱)"""
if mod is None:
return None
return mod.FuncHead
def module_add_global(mod: rawptr[LLVMModule], gv: rawptr[GlobalVariable]):
mod.add_global(gv)
def module_set_target(mod: rawptr[LLVMModule], triple: str):
mod.set_target(triple)
def module_set_datalayout(mod: rawptr[LLVMModule], layout: str):
mod.set_datalayout(layout)
# ============================================================
# DI debug metadata(同形保留;MLIR 打印阶段忽略,仅记录模型)
# ============================================================
def module_add_di_node(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
text: str) -> int:
"""Append a DI metadata node; return its !N id (-1 on failure)"""
return mod.add_di_node(pool, text)
def module_add_named_metadata(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
name: str, refs: str):
"""Register named metadata "!<name> = !{ <refs> }" """
mod.add_named_metadata(pool, name, refs)
# ============================================================
# 命名结构体类型管理
# ============================================================
# ============================================================
# _is_opaque_struct - 检查类型是否是 opaque 结构体
#
# opaque 结构体特征: Struct 类型,fcount == -1(哨兵标记,跨模块前向
# 声明,仅名称无字段定义)。通过 Struct(pool, None, -1, name) 创建,
# IR 输出为 %"name" = type opaque。注意区分空结构体(fcount==0,
# fields==None,无字段类)——它是合法完整定义(= type {},可 alloca)。
# 用于在 module_add_named_type 中区分 opaque 声明和完整定义,
# 确保 opaque 声明插入链表头部(在使用之前声明)。
#
# Returns: 1=是 opaque / 0=否
# ============================================================
def _is_opaque_struct(ty: rawptr[LLVMType]) -> int:
"""检查类型是否是 opaque 结构体
opaque 是跨模块前向声明(仅名称无字段定义),由
_scan_type_for_cross_module_refs 创建,fcount=-1 哨兵标记。
空结构体(fcount==0, fields==None,无字段类)是合法完整定义
(LLVM {} 类型,可 alloca),必须与 opaque 区分——此前混淆导致
空类结构体被输出为 "= type opaque",alloca 报 Cannot allocate
unsized type。
"""
if ty is None:
return 0
match ty:
case LLVMType.Struct(fields, fcount, name):
# opaque: fcount == -1(哨兵)
if fcount < 0:
return 1
return 0
case _:
return 0
def module_add_named_type(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
ty: rawptr[LLVMType]):
mod.add_named_type(pool, ty)
def module_set_named_type_packed(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
ty: rawptr[LLVMType], is_packed: int):
"""按名称设置命名结构体的 packed 标记(MLIR 输出 !llvm.struct<"name", packed (…)>)。
编译器解析 @c.Attribute(t.attr.packed) 后调用,使 llc 按 packed 布局
(无字段填充)计算字段偏移/GEP。MLIR 侧由打印期登记表携带该标记
(_MlirPublishNamedTypes 读 NamedTypeNode.IsPacked),使用处内联形态加
`packed ` 关键字。ty 为命名 Struct 类型(按 get_struct_name 定位已注册节点);
未找到则静默跳过(opaque/跨模块 stub 场景无布局需求)。匿名结构体
(Name 为 None)在此直接返回 —— packed 标记无法也没有位置挂在匿名结构体上。
"""
if mod is None or ty is None:
return
name: str = get_struct_name(ty)
if name is None:
return
cur: rawptr[NamedTypeNode] = mod.NamedTypeHead
cnt_p: int = 0
while cur is not None:
cnt_p += 1
if cnt_p > 100000:
return
if cur.Ty is not None:
existing_name: str = get_struct_name(cur.Ty)
if existing_name is not None and string.strcmp(existing_name, name) == 0:
cur.IsPacked = is_packed
return
cur = cur.Next
# ============================================================
# module_ensure_opaque_for_type - 为类型中的跨模块结构体引用添加 opaque 声明
#
# 递归扫描 LLVMType 树,当遇到 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时,
# 如果该名称未在模块中定义,则添加 opaque 声明(%"name" = type opaque)。
# 这解决了跨模块结构体引用导致的 llc "use of undefined type" 错误。
# ============================================================
def _is_type_defined_in_module(mod: rawptr[LLVMModule], name: str) -> int:
return mod._is_type_defined_in_module(name)
def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy*,
mod: rawptr[LLVMModule],
ty: rawptr[LLVMType],
depth: int = 0):
"""递归扫描类型树,为跨模块结构体引用添加 opaque 声明"""
if ty is None or mod is None or pool is None:
return
# 深度守卫:自引用结构体(链表/树节点,如 Node{...; Ptr(Node) next})
# 会无限递归栈溢出,与其他递归(TypePrint/DeepCopyType)一致设上限
if depth >= 100:
return
match ty:
case LLVMType.Ptr(pointee):
if pointee is not None:
match pointee:
case LLVMType.Struct(sfields, sfcount, sname):
if sname is not None:
# 检查是否为跨模块引用(名称包含 '.')
if string.strchr(sname, 46) is not None:
# 检查是否已在模块中定义
if _is_type_defined_in_module(mod, sname) == 0:
# 创建 opaque 类型并添加到模块
# fcount=-1 哨兵标记 opaque(区别于空结构体 {},
# 后者 fcount==0 是合法完整定义,可 alloca)
opaque_ty: rawptr[LLVMType] = Struct(pool, None, -1, sname)
if opaque_ty is not None:
module_add_named_type(mod, pool, opaque_ty)
# 递归扫描结构体字段中的跨模块引用
# 例如 TypeRegistry{HashTable*, MemManager*} 中的 HashTable 引用
if sfields is not None:
fld: rawptr[ParamNode] = sfields
fld_steps: sizet = 0
while fld is not None:
fld_steps += 1
if fld_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: struct fields(sname=%s,dump 终止)\n",
sname)
stdlib.exit(1)
if fld.Ty is not None:
_scan_type_for_cross_module_refs(pool, mod, fld.Ty, depth + 1)
fld = fld.Next
case _:
_scan_type_for_cross_module_refs(pool, mod, pointee, depth + 1)
case LLVMType.Struct(sfields2, sfcount2, sname2):
# 顶层结构体(非指针):递归扫描字段
if sfields2 is not None:
fld2: rawptr[ParamNode] = sfields2
fld2_steps: sizet = 0
while fld2 is not None:
fld2_steps += 1
if fld2_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: struct fields2(sname=%s,dump 终止)\n",
sname2)
stdlib.exit(1)
if fld2.Ty is not None:
_scan_type_for_cross_module_refs(pool, mod, fld2.Ty, depth + 1)
fld2 = fld2.Next
case LLVMType.Array(elem_ty, acount):
if elem_ty is not None:
_scan_type_for_cross_module_refs(pool, mod, elem_ty, depth + 1)
case _:
pass
def module_ensure_opaque_for_type(mod: rawptr[LLVMModule],
pool: memhub.MemBuddy*,
ty: rawptr[LLVMType]):
mod.ensure_opaque_for_type(pool, ty)
# ============================================================
# 全局变量属性设置
# ============================================================
def global_set_constant(gv: rawptr[GlobalVariable], is_const: int):
"""设置全局变量是否为常量"""
if gv is None: return
gv.IsConstant = is_const
def global_set_linkage(gv: rawptr[GlobalVariable], linkage: str):
"""设置链接类型 (private/internal/external/... )"""
if gv is None: return
gv.Linkage = linkage
def global_set_unnamed_addr(gv: rawptr[GlobalVariable], level: int):
"""设置 unnamed_addr 属性 (0=none, 1=unnamed_addr, 2=local_unnamed_addr)"""
if gv is None: return
gv.UnnamedAddr = level
def global_set_section(gv: rawptr[GlobalVariable], section: str):
"""设置段名"""
if gv is None: return
gv.Section = section
# ============================================================
# 输出模式常量
# ============================================================
define OUTPUT_FULL = 0# 完整 IR(stub + text)
define OUTPUT_STUB = 1# 仅声明(header + 类型定义 + declare + external global)
define OUTPUT_TEXT = 2# 仅代码(define + global 带初值)
# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast)。
# 任何一条链(函数/全局/命名类型/结构体字段)的 Next 被越界写破坏成环时,
# 遍历会无限循环导致 tpv.exe 挂起(builtins.vp dump 卡死根因排查)。
# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位"
# (对齐 memhub.self_check 的 max_steps 哲学)。
define IR_DUMP_CHAIN_LIMIT = 100000
_MlirModOutTrunc: int = 0
# ============================================================
# LLVMModulePrint: 将模块序列化为 .ll 文本
#
# mode: OUTPUT_FULL=完整, OUTPUT_STUB=仅声明, OUTPUT_TEXT=仅代码
# ============================================================
def LLVMModulePrint(buf: str, size: sizet,
mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
mode: int):
global _MlirModOutTrunc
_MlirModOutTrunc = 0
mod.PrintModule(buf, size, pool, mode)
if _MlirModOutTrunc != 0:
_MlirModOutTrunc = 0
MlirFail("MLIR 渲染: 模块文本超出输出缓冲被截断(需增大 dump_ir 缓冲)")
# ============================================================
# 全局变量渲染(MLIR)
#
# 形态:
# llvm.mlir.global internal constant @.str("hello") : !llvm.array<6 x i8>
# llvm.mlir.global @counter(0 : i32) : i32
# llvm.mlir.global external @x : i32
# linkage/constant/unnamed_addr/section 映射为对应关键字/属性。
# 初值有两条路径(判定见 _MlirGlobalInitNeedsRegion):
# * 属性路径:整数字面量、浮点、cstring、字符串字面量、纯标量数组;
# * region 路径:根级裸 null/zeroinitializer(属性形态 `(zeroinitializer)`
# 被 mlir-opt 拒绝)、结构体/vtable、含符号引用的聚合、指针型初值 →
# 空值括号 + `: 类型 {` 内的 initializer region
# (叶子 op + undef + insertvalue 链 + return)。
# 两条路径都覆盖不到的形态一律 MlirFail **响亮报错**,绝不静默输出错误 MLIR。
# ============================================================
def _MlirGlobalLine(buf: str, size: sizet,
pool: memhub.MemBuddy*, gv: rawptr[GlobalVariable],
stub: int):
"""渲染一条 llvm.mlir.global(stub=1 时输出 external 声明形态)"""
if gv is None:
return
_append_cstr(buf, size, "llvm.mlir.global ")
# 链接类型
if stub != 0:
_append_cstr(buf, size, "external ")
elif gv.Linkage is not None:
# external + 初值是非法 IR,去掉 external
if gv.Initializer is not None and string.strcmp(gv.Linkage, "external") == 0:
pass
else:
_append_cstr(buf, size, gv.Linkage)
_append_cstr(buf, size, " ")
if stub == 0:
# unnamed_addr
if gv.UnnamedAddr == 1:
_append_cstr(buf, size, "unnamed_addr ")
elif gv.UnnamedAddr == 2:
_append_cstr(buf, size, "local_unnamed_addr ")
# constant / 普通 global(MLIR 中非 constant 无需关键字)
if gv.IsConstant == 1:
_append_cstr(buf, size, "constant ")
# 名字
_append_cstr(buf, size, "@")
if gv.Name is not None:
if _ll_name_needs_quote(gv.Name) != 0:
_append_cstr(buf, size, "\"")
_append_cstr(buf, size, gv.Name)
_append_cstr(buf, size, "\"")
else:
_append_cstr(buf, size, gv.Name)
# 初值:聚合/含符号引用/根级裸零值形态改用 initializer region
# (见 _MlirGlobalInitNeedsRegion)
region: int = 0
if stub == 0 and gv.Initializer is not None:
region = _MlirGlobalInitNeedsRegion(gv.Initializer)
if region == 0:
_append_cstr(buf, size, "(")
_MlirGlobalInit(buf, size, pool, gv)
_append_cstr(buf, size, ")")
else:
# region 形态:值括号为空(初值由 region 内的 insertvalue 链构造)
_append_cstr(buf, size, "()")
else:
# 无初值(stub=1 的 external 声明 / 无初值全局):**值括号必须保留**
# ⚠ mlir-opt 22.1.8 实测:`llvm.mlir.global external @x : i32` 报
# "expected '('";`llvm.mlir.global external @x() : i32` exit 0。
_append_cstr(buf, size, "()")
# 属性字典(section / alignment):**必须在类型之前**(值之后)输出。
# ⚠ mlir-opt 22.1.8 实测确立(违反即解析失败):
# * 正确 = `@"s"("ab\00") {section = "data", alignment = 16 : i64} : !llvm.array<3 x i8>`
# (exit 0);
# * 错误 = 字典放在**类型之后**(`… : !llvm.array<3 x i8> {section = "data"}`
# 被当成新 op,报 "Dialect `' not found for custom op 'section'");
# * `unnamed_addr` 是**关键字**(见上,在名字之前输出),**不能**放进字典
# (放进字典报 "attribute 'unnamed_addr' occurs more than once")。
if stub == 0 and (gv.Section is not None or gv.Align > 0):
_append_cstr(buf, size, " {")
if gv.Section is not None:
_append_cstr(buf, size, "section = \"")
_append_cstr(buf, size, gv.Section)
_append_cstr(buf, size, "\"")
if gv.Align > 0:
_append_cstr(buf, size, ", ")
if gv.Align > 0:
ab: str = pool.alloc(48)
if ab is not None:
viperlib.snprintf(ab, 48, "alignment = %d : i64", gv.Align)
_append_cstr(buf, size, ab)
_append_cstr(buf, size, "}")
# 类型
_append_cstr(buf, size, " : ")
ty_buf: str = pool.alloc(4096)
if ty_buf is not None:
ty_buf[0] = '\0'
TypePrint(ty_buf, 4096, gv.Ty, pool)
_append_cstr(buf, size, ty_buf)
# initializer region(顶层聚合/指针初值:叶子 op + undef + insertvalue 链 + return)
if region != 0:
_append_cstr(buf, size, " {\n")
_MlirGlobalInitRegion(buf, size, pool, gv)
_append_cstr(buf, size, "}")
_append_cstr(buf, size, "\n")
def _MlirGlobalInit(buf: str, size: sizet,
pool: memhub.MemBuddy*, gv: rawptr[GlobalVariable]):
"""渲染全局初值(MLIR attribute 文本)"""
x: str = gv.Initializer
if x is None:
return
if x[0] == 'c' and x[1] == '"':
_MlirCStringToMlir(buf, size, x, pool)
return
if x[0] == '"':
# 已是字符串字面量
_append_cstr(buf, size, x)
return
if x[0] == '[':
# 整型/浮点数组: [i8 1, i8 2] → [1 : i8, 2 : i8]
# ⚠ 实测 mlir-opt 22.1.8:
# - `dense<[...] : !llvm.array<…>>` **非法**("elements literal must be
# a shaped type":dense 只接受 builtin shaped 类型 tensor/vector);
# - `[1, 2]`(元素无类型注解)仅在元素类型等于默认 i64 时可用;
# - 正确形态 = 数组属性直接放进 value 括号,元素带 `值 : 类型` 注解。
# 全局类型由 _MlirGlobalLine 在括号后统一输出,此处不再重复。
_MlirArrayDenseAt(buf, size, x, pool)
return
if string.strcmp(x, "null") == 0 or string.strcmp(x, "zeroinitializer") == 0:
# 根级裸零值现由 region 路径处理(_MlirGlobalInitNeedsRegion 判 1),
# 属性形态 `(zeroinitializer)` 已被 mlir-opt 拒绝,此处仅为兜底保留。
_append_cstr(buf, size, "zeroinitializer")
return
if x[0] == '@':
# 符号引用初值(顶层 @name 现由 region 路径处理,这里是属性路径的兜底)
_append_cstr(buf, size, x)
return
c0: int = x[0]
if c0 == '-' or (c0 >= '0' and c0 <= '9'):
# 整数/浮点字面量: "<文本> : <MLIR 类型>"
_append_cstr(buf, size, x)
_append_cstr(buf, size, " : ")
ty_buf: str = pool.alloc(4096)
if ty_buf is not None:
ty_buf[0] = '\0'
TypePrint(ty_buf, 4096, gv.Ty, pool)
_append_cstr(buf, size, ty_buf)
return
# 属性路径未覆盖且未走 region 的残留形态:响亮报错,绝不静默输出错误 MLIR
# (结构体/vtable、含符号引用的聚合、指针型初值已由 _MlirGlobalInitRegion
# 在 region 路径渲染,不再落到这里)
MlirFail("MLIR 渲染: 全局初值形态 v1 不支持(属性路径与 initializer region 均未覆盖)")
def _MlirCStringToMlir(buf: str, size: sizet, src: str, pool: memhub.MemBuddy*):
"""c"hello\\00" → "hello\\00"(仅去掉 'c' 前缀,转义内容原样保留)
⚠ 实测 mlir-opt 22.1.8:`llvm.mlir.global` 的字符串属性**长度必须等于**
声明的 i8 数组长度,否则报
"op requires an i8 array type of the length equal to that of the string attribute"。
TPV 的字符串常量类型含尾随 NUL([N+1 x i8]),故属性里**必须保留 \\00**。
绝不能改为剥掉类型里的 NUL —— 那会丢掉 C 字符串终止符、破坏 ABI。
"""
p: str = src + 1 # 跳过 'c',指向 '"'
if p[0] != '"':
MlirFail("MLIR 渲染: 全局 c 字符串初值形态非法")
_append_cstr(buf, size, p)
def _MlirArrayDenseAt(buf: str, size: sizet, src: str,
pool: memhub.MemBuddy*) -> str:
"""LLVM 数组字面量 → MLIR dense 内容(嵌套递归),返回新游标"""
if src is None or src[0] != '[':
MlirFail("MLIR 渲染: 全局数组初值形态 v1 不支持")
p: str = src + 1
_append_cstr(buf, size, "[")
n: int = 0
g: int = 0
while p[0] != '\0':
g += 1
if g > 4096:
MlirFail("MLIR 渲染: 全局数组元素过多")
p = _mlir_ws(p)
if p[0] == ']':
p = p + 1
break
if p[0] == '\0':
break
if n > 0:
_append_cstr(buf, size, ", ")
if p[0] == '[':
p = _MlirArrayDenseAt(buf, size, p, pool)
else:
tb: str = pool.alloc(512)
if tb is None:
MlirFail("MLIR 渲染: 数组元素类型缓冲分配失败")
p = _mlir_read_type(p, tb, 512)
p = _mlir_ws(p)
vb: str = pool.alloc(512)
if vb is None:
MlirFail("MLIR 渲染: 数组元素值缓冲分配失败")
p = _mlir_read_value(p, vb, 512)
_append_cstr(buf, size, vb)
# 元素必须带类型注解:MLIR 数组属性元素默认按 i64 解释,
# i8/i32/指针等元素不注解会与全局类型不匹配(实测通过形态:
# `[1 : i8, 2 : i8] : !llvm.array<2 x i8>`)。
_append_cstr(buf, size, " : ")
mtb: str = pool.alloc(4096)
if mtb is None:
MlirFail("MLIR 渲染: 数组元素 MLIR 类型缓冲分配失败")
mtb[0] = '\0'
MlirTypeText(mtb, 4096, tb, pool)
_append_cstr(buf, size, mtb)
n += 1
p = _mlir_ws(p)
if p[0] == ',':
p = p + 1
_append_cstr(buf, size, "]")
return p
# ============================================================
# 全局初值的 initializer region 渲染(结构体/vtable 等聚合与指针初值)
#
# 实测 mlir-opt 22.1.8 通过形态(internal 与 internal constant 均可):
# llvm.mlir.global constant @vt() : !llvm.struct<(!llvm.ptr, !llvm.ptr)> {
# %v0 = llvm.mlir.addressof @"sha1.m" : !llvm.ptr
# %undef = llvm.mlir.undef : !llvm.struct<(!llvm.ptr, !llvm.ptr)>
# %s1 = llvm.insertvalue %v0, %undef[0] : !llvm.struct<(!llvm.ptr, !llvm.ptr)>
# %v1 = llvm.mlir.zero : !llvm.ptr
# %s2 = llvm.insertvalue %v1, %s1[1] : !llvm.struct<(!llvm.ptr, !llvm.ptr)>
# llvm.return %s2 : !llvm.struct<(!llvm.ptr, !llvm.ptr)>
# }
#
# 实现要点:
# * 叶子 op(addressof / zero / constant)**先集中发射**,随后 undef 与
# insertvalue 链;叶子 op 与插入 op 分写两个缓冲,最后按此顺序拼装;
# * 每个 insertvalue 的**类型后缀始终是顶层聚合类型**,下标是"初值树 ×
# 类型树"并行遍历得到的下标路径(嵌套聚合用多下标,如 [0, 1]);
# * 元素 MLIR 类型来自**全局类型树**(Struct 字段链 / Array 元素类型)经
# TypePrint 取文本;初值文本里的类型记号只用于推进游标,不作类型依据;
# * bitcast/getelementptr 常量表达式整体视为"符号引用"——不透明指针下
# bitcast 恒等、GEP 取首元素地址等价于 addressof,故不还原 bitcast;
# * 符号名一律经 _MlirSymRefAppend(先剥 LLVM 口径引号,再按 MLIR 口径重加);
# * 不支持的叶子形态一律 MlirFail(带原始文本),绝不静默输出错误 MLIR。
# ============================================================
define GV_REGION_BUF_SIZE = 32768
# 下标路径复用栈:每个递归深度一个槽(顶层 path="" → 深度 d 的路径写在槽 d)。
# 深度上限与 _MlirGvWalk 的 64 层守卫一致,留余量取 72;每槽 512 字节足够
# (64 层 × " , NN" ≈ 400 字节)。
define GV_PATH_SLOT = 512
define GV_PATH_MAX = 72
# region 渲染期状态(单线程递归渲染,逐全局重置)
_MlirGvLeafBuf: str = None # 叶子 op 缓冲
_MlirGvInsBuf: str = None # insertvalue 链缓冲
_MlirGvTopType: str = None # 顶层聚合 MLIR 类型文本
_MlirGvLast: str = None # 当前节点的 SSA 名
_MlirGvPrev: str = None # insertvalue 链前一结果 SSA 名
_MlirGvPool: memhub.MemBuddy* = None
_MlirGvLeafSeq: int = 0
_MlirGvInsSeq: int = 0
# ---- region 渲染的**复用**草稿缓冲(惰性分配一次,跨全局/跨节点共享)----
#
# 此前每个全局、每个初值节点都从模块子分配器 alloc 新缓冲且从不释放:
# 每全局 leaf/ins 各 32 KiB、每节点 4 KiB ⇒ 结构体/vtable 全局多的模块
# (MLIRLiteTest #12)把 ~2 MiB 子池耗尽,报 "零初值类型缓冲分配失败"。
# region 渲染是单线程顺序的,草稿内容用完即弃 ⇒ 分配一次复用即可。
_MlirGvSbRead1: str = None # 512B:类型前缀记号读取草稿
_MlirGvSbRead2: str = None # 512B:叶子类型记号读取草稿
_MlirGvSbName: str = None # 512B:符号名 / 常量表达式符号名草稿
_MlirGvSbVal: str = None # 512B:标量初值字面量草稿
_MlirGvTyBuf: str = None # 4096B:元素 MLIR 类型文本(zero/const)
_MlirGvNameBuf: str = None # 32B:SSA 名生成草稿
_MlirGvPathStack: str = None # GV_PATH_MAX × GV_PATH_SLOT:下标路径复用栈
def MlirGvResetScratch():
"""使 region 渲染的复用草稿失效(下次 EnsureScratch 按当前池重新分配)
草稿只在**单次 PrintModule** 内经惰性分配复用(跨全局/跨节点共享);
打印结束由 PrintModule 调用本函数置空 ⇒ 绝不复用上一次打印的指针
(上一次的池可能已释放,或同一地址被新池复用 → 悬空写穿)。
"""
global _MlirGvLeafBuf
_MlirGvLeafBuf = None
def _MlirGvEnsureScratch(pool: memhub.MemBuddy*) -> int:
"""惰性分配本次 PrintModule 的复用草稿缓冲(跨全局/跨节点共享),返回 1=就绪"""
global _MlirGvLeafBuf, _MlirGvInsBuf, _MlirGvTopType, _MlirGvLast, _MlirGvPrev
global _MlirGvSbRead1, _MlirGvSbRead2, _MlirGvSbName, _MlirGvSbVal
global _MlirGvTyBuf, _MlirGvNameBuf, _MlirGvPathStack
if _MlirGvLeafBuf is not None:
return 1
lb: str = pool.alloc(GV_REGION_BUF_SIZE)
ib: str = pool.alloc(GV_REGION_BUF_SIZE)
tb: str = pool.alloc(4096)
la: str = pool.alloc(32)
pr: str = pool.alloc(32)
r1: str = pool.alloc(512)
r2: str = pool.alloc(512)
nm: str = pool.alloc(512)
vl: str = pool.alloc(512)
tyb: str = pool.alloc(4096)
nb: str = pool.alloc(32)
ps: str = pool.alloc(GV_PATH_SLOT * GV_PATH_MAX)
if lb is None or ib is None or tb is None or la is None or pr is None \
or r1 is None or r2 is None or nm is None or vl is None \
or tyb is None or nb is None or ps is None:
MlirFail("MLIR 渲染: 全局 region 缓冲分配失败")
lb[0] = '\0'; ib[0] = '\0'; tb[0] = '\0'; la[0] = '\0'; pr[0] = '\0'
r1[0] = '\0'; r2[0] = '\0'; nm[0] = '\0'; vl[0] = '\0'
tyb[0] = '\0'; nb[0] = '\0'; ps[0] = '\0'
_MlirGvLeafBuf = lb
_MlirGvInsBuf = ib
_MlirGvTopType = tb
_MlirGvLast = la
_MlirGvPrev = pr
_MlirGvSbRead1 = r1
_MlirGvSbRead2 = r2
_MlirGvSbName = nm
_MlirGvSbVal = vl
_MlirGvTyBuf = tyb
_MlirGvNameBuf = nb
_MlirGvPathStack = ps
return 1
def _MlirGlobalInitNeedsRegion(x: str) -> int:
"""判定全局初值是否改走 initializer region(1=region / 0=属性路径)
属性路径(已实测通过,不得回退)仅覆盖: c 字符串、字符串字面量、
标量字面量、**纯标量**数组。
其余形态一律 region: **根级裸 `null` / `zeroinitializer`**、结构体 `{…}`、
含符号引用(@)/结构体/MLIR 类型前缀的数组、顶层裸符号引用 `@name`、
指针/聚合类型前缀开头的初值(如 `i8* null`、`i8* bitcast (…)`、
`i8* getelementptr …`)。
"""
if x is None or x[0] == '\0':
return 0
if x[0] == 'c' and x[1] == '"':
return 0
if x[0] == '"':
return 0
if x[0] == '[':
return _MlirGvArrayNeedsRegion(x)
if x[0] == '{':
return 1
if string.strcmp(x, "null") == 0 or string.strcmp(x, "zeroinitializer") == 0:
# 根级裸零值改走 region:属性形态 `(zeroinitializer)` 被 mlir-opt 拒绝
# (实测 22.1.8: "expected attribute value");region 输出
# `llvm.mlir.zero : <顶层类型>` + 直接 `llvm.return`。
return 1
if x[0] == '@':
return 1
c0: int = x[0]
if c0 == '-' or (c0 >= '0' and c0 <= '9'):
return 0
return 1
def _MlirGvArrayNeedsRegion(x: str) -> int:
"""数组初值是否必须走 initializer region(1=region / 0=属性路径)
出现以下任一记号即返回 1:
- 符号引用 '@':属性路径无法表达地址引用;
- 结构体 '{':聚合元素;
- MLIR 类型前缀 '!':如嵌套数组元素的子数组前缀 `!llvm.array<…>`。
属性路径(_MlirArrayDenseAt)只认"元素为 `值 : 标量类型`"的稠密数组,
内层类型前缀与子 `zeroinitializer` 都无法表达,必须交给 region 的
insertvalue 链。扫描跳过字符串字面量,避免把 LLVM 字符串里的 '{'/'!' 误判。
"""
p: str = x
while p[0] != '\0':
ch: int = p[0]
if ch == '"':
p = p + 1
while p[0] != '\0' and p[0] != '"':
# LLVM 字符串用 \xx 十六进制转义;'\' 跳过其后一字节
if p[0] == 0x5C and p[1] != '\0':
p = p + 1
p = p + 1
if p[0] == '"':
p = p + 1
continue
if ch == '@' or ch == '{' or ch == '!':
return 1
p = p + 1
return 0
def _MlirGlobalInitRegion(buf: str, size: sizet,
pool: memhub.MemBuddy*, gv: rawptr[GlobalVariable]):
"""渲染 initializer region 的体(2 空格缩进的行序列,不含最外层大括号)
每行都带 '\\n';调用方在末尾补 '}',模块层再统一缩进 2 空格。
"""
global _MlirGvPool, _MlirGvLeafSeq, _MlirGvInsSeq
x: str = gv.Initializer
if x is None:
return
# 复用缓冲(惰性分配一次,跨全局共享)——此前逐全局 alloc 且不释放,
# 结构体/vtable 全局多时耗尽模块子分配器。
_MlirGvEnsureScratch(pool)
topbuf: str = _MlirGvTopType
leafbuf: str = _MlirGvLeafBuf
insbuf: str = _MlirGvInsBuf
lastbuf: str = _MlirGvLast
prevbuf: str = _MlirGvPrev
topbuf[0] = '\0'
leafbuf[0] = '\0'
insbuf[0] = '\0'
lastbuf[0] = '\0'
prevbuf[0] = '\0'
TypePrint(topbuf, 4096, gv.Ty, pool)
_MlirGvPool = pool
_MlirGvLeafSeq = 0
_MlirGvInsSeq = 0
is_agg: int = 0
match gv.Ty:
case LLVMType.Struct(sf, sfc, sn):
is_agg = 1
case LLVMType.Array(ae, ac):
is_agg = 1
case _:
is_agg = 0
if is_agg != 0:
# insertvalue 链的起点: 顶层 undef
string.strcpy(prevbuf, "%undef")
_MlirGvWalk(x, gv.Ty, "", 0)
# 叶子 op 集中发射,再 undef + insertvalue 链
_append_cstr(buf, size, leafbuf)
if is_agg != 0:
_append_cstr(buf, size, " %undef = llvm.mlir.undef : ")
_append_cstr(buf, size, topbuf)
_append_cstr(buf, size, "\n")
_append_cstr(buf, size, insbuf)
_append_cstr(buf, size, " llvm.return ")
if lastbuf[0] != '\0':
_append_cstr(buf, size, lastbuf)
else:
_append_cstr(buf, size, "%undef")
_append_cstr(buf, size, " : ")
_append_cstr(buf, size, topbuf)
_append_cstr(buf, size, "\n")
def _MlirGvWalk(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str:
"""并行遍历"初值树 × 类型树"的一个节点,返回新游标
结果 SSA 名写入 _MlirGvLast(叶子=物化名;聚合=最后一个 insertvalue 名;
空聚合= %undef)。下标路径 path 形如 "" / "0" / "0, 1"。
"""
if depth > 64:
MlirFail("MLIR 渲染: 全局初值嵌套过深(> 64 层)")
if ty is None:
MlirFail("MLIR 渲染: 全局初值类型树缺失(无法确定元素 MLIR 类型)")
p = _mlir_ws(p)
if p[0] == '\0':
MlirFail("MLIR 渲染: 全局初值文本意外结束")
# 显式数组类型前缀(如字段写成 "[3 x i8] [i8 1, i8 2]")先整体读掉
if p[0] == '[' and _MlirGvIsArrayType(p) != 0:
tb: str = _MlirGvSbRead1
p = _mlir_read_type(p, tb, 512)
p = _mlir_ws(p)
# 显式结构体类型前缀(如 llvm.global_ctors 元素的
# "{ i32, void ()*, i8* } { i32 65535, … }")先整体读掉;`{…}` 既可能是
# 类型也可能是字面量,靠"其后是否紧跟另一个 `{`"区分(见 _MlirGvIsStructType)
if p[0] == '{' and _MlirGvIsStructType(p) != 0:
stb: str = _MlirGvSbRead1
p = _mlir_read_type(p, stb, 512)
p = _mlir_ws(p)
# MLIR 形态聚合类型前缀(`!llvm.array<…>` / `!llvm.struct<…>`)后紧跟聚合
# 字面量时先整体读掉:来自上层用 mlirlite.TypePrint 生成的内层类型(如嵌套
# 数组初始化器的子数组前缀 `!llvm.array<16 x i8>[i8 0, …]`,见
# TranslatorGenMixin._build_array_initializer_text),上方 LLVM 形态前缀
# 判据(_MlirGvIsArrayType/_MlirGvIsStructType)认不出 '!' 开头
if p[0] == '!':
mtb: str = _MlirGvSbRead1
qm: str = _mlir_read_type(p, mtb, 512)
qm = _mlir_ws(qm)
if qm[0] == '[' or qm[0] == '{':
p = qm
if p[0] == '{':
return _MlirGvWalkStruct(p, ty, path, depth)
if p[0] == '[':
return _MlirGvWalkArray(p, ty, path, depth)
return _MlirGvWalkLeaf(p, ty, path, depth)
def _MlirGvIsArrayType(p: str) -> int:
"""判断游标处是否为 LLVM 数组类型前缀 "[N x TY]"(用于与数组字面量区分)"""
if p is None or p[0] != '[':
return 0
q: str = p + 1
nd: int = 0
while q[0] >= '0' and q[0] <= '9':
q = q + 1
nd += 1
if nd == 0 or q[0] != ' ':
return 0
q = q + 1
if q[0] != 'x' or q[1] != ' ':
return 0
return 1
def _MlirGvIsStructType(p: str) -> int:
"""判断游标处是否为 LLVM 结构体**类型前缀** `{ … } { … }`(与结构体字面量区分)
`{ … }` 既是结构体类型词法、也是结构体字面量词法,单看首个花括号无法区分。
可靠判据:把首个花括号组**按深度配平**读完(跳过引号内文本,LLVM 字符串里
的 `{`/`}` 是普通字符)后,下一个非空白记号若仍是 '{',则前一花括号组是
类型前缀、后一花括号组才是初值;否则该花括号组本身就是初值——同一元素的
花括号组之后只会是 ',' / ']' / '}' / 结束,绝不会紧跟 '{'。
"""
if p is None or p[0] != '{':
return 0
q: str = p
dep: int = 0
while q[0] != '\0':
ch: int = q[0]
if ch == '"':
q = q + 1
while q[0] != '\0' and q[0] != '"':
if q[0] == 0x5C and q[1] != '\0':
q = q + 1
q = q + 1
if q[0] == '"':
q = q + 1
continue
if ch == '{':
dep += 1
elif ch == '}':
dep -= 1
if dep == 0:
q = q + 1
break
q = q + 1
if dep != 0:
return 0
q = _mlir_ws(q)
if q[0] == '{':
return 1
return 0
def _MlirGvPathChild(path: str, idx: int, depth: int) -> str:
"""由父下标路径与子序号拼出子下标路径(顶层 path="" → "0")
写入按递归深度分槽的复用栈(槽 = depth):祖先路径占 0..depth-1,
本层写槽 depth、子层写槽 depth+1,互不覆盖;避免逐节点 alloc 泄漏。
"""
if depth < 0 or depth >= GV_PATH_MAX:
MlirFail("MLIR 渲染: 下标路径深度越界")
nb: str = _MlirGvPathStack + depth * GV_PATH_SLOT
if path[0] == '\0':
viperlib.snprintf(nb, GV_PATH_SLOT, "%d", idx)
else:
viperlib.snprintf(nb, GV_PATH_SLOT, "%s, %d", path, idx)
return nb
def _MlirGvWalkStruct(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str:
"""结构体初值 { 元素, 元素, … }: 字段类型取类型树的 Struct 字段链"""
fields: rawptr[ParamNode] = None
match ty:
case LLVMType.Struct(sf, sfc, sn):
fields = sf
case _:
MlirFail("MLIR 渲染: 初值为结构体,但全局类型不是结构体")
cur: rawptr[ParamNode] = fields
p = p + 1
idx: int = 0
done: int = 0
guard: int = 0
while done == 0:
guard += 1
if guard > 4096:
MlirFail("MLIR 渲染: 结构体初值元素过多")
p = _mlir_ws(p)
if p[0] == '\0':
MlirFail("MLIR 渲染: 结构体初值缺少 '}'")
if p[0] == '}':
p = p + 1
done = 1
elif p[0] == ',':
p = p + 1
else:
fty: rawptr[LLVMType] = None
if cur is not None:
fty = paramnode_get_ty(cur)
p = _MlirGvWalk(p, fty, _MlirGvPathChild(path, idx, depth), depth + 1)
if cur is not None:
cur = paramnode_get_next(cur)
idx += 1
if idx == 0:
string.strcpy(_MlirGvLast, "%undef")
return p
def _MlirGvWalkArray(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str:
"""数组初值 [ 元素, 元素, … ]: 元素类型取类型树的 Array 元素类型"""
elem_ty: rawptr[LLVMType] = None
match ty:
case LLVMType.Array(ae, ac):
elem_ty = ae
case _:
MlirFail("MLIR 渲染: 初值为数组,但全局类型不是数组")
p = p + 1
idx: int = 0
done: int = 0
guard: int = 0
while done == 0:
guard += 1
if guard > 65536:
MlirFail("MLIR 渲染: 数组初值元素过多")
p = _mlir_ws(p)
if p[0] == '\0':
MlirFail("MLIR 渲染: 数组初值缺少 ']'")
if p[0] == ']':
p = p + 1
done = 1
elif p[0] == ',':
p = p + 1
else:
p = _MlirGvWalk(p, elem_ty, _MlirGvPathChild(path, idx, depth), depth + 1)
idx += 1
if idx == 0:
string.strcpy(_MlirGvLast, "%undef")
return p
def _MlirGvIsBareValue(p: str) -> int:
"""游标处是否**直接**是值(无 LLVM/MLIR 类型前缀)
TPV 的根初值可能是无类型前缀的裸形态(如字符串指针全局的
`getelementptr inbounds ([N x i8], … @.str.x …)`、对象 storage 引用 `@name`);
聚合元素则通常是 `<类型> <值>`。类型记号从不以 `@`/数字/`-`/`.` 开头,
也不会有 `bitcast`/`getelementptr`/`null`/`zeroinitializer` 这些名字,
故该判据无歧义。
"""
if p is None or p[0] == '\0':
return 0
if p[0] == '@':
return 1
if string.strncmp(p, "bitcast", 7) == 0:
return 1
if string.strncmp(p, "getelementptr", 13) == 0:
return 1
if string.strncmp(p, "null", 4) == 0:
return 1
if string.strncmp(p, "zeroinitializer", 15) == 0:
return 1
c0: int = p[0]
if c0 == '-' or (c0 >= '0' and c0 <= '9') or c0 == '.':
return 1
return 0
def _MlirGvWalkLeaf(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str:
"""叶子元素 `<LLVM 类型> <值>`: 值解析为 符号引用 / null / 标量字面量
元素 MLIR 类型不取初值文本里的类型记号,而取类型树(ty)。
顶层叶子(depth == 0,整棵初值即该叶子,如指针型全局)不走 insertvalue
链;只有位于聚合内部的叶子才需要插入。
"""
p = _mlir_ws(p)
if _MlirGvIsBareValue(p) == 0:
# 元素自带类型记号(`i8* null` / `!llvm.ptr null` 等):只用于推进游标
tb: str = _MlirGvSbRead2
p = _mlir_read_type(p, tb, 512)
p = _mlir_ws(p)
if p[0] == '\0':
MlirFail("MLIR 渲染: 叶子初值只有类型、缺少值")
if p[0] == '@':
# 裸符号引用(剥 LLVM 引号 → 经 _MlirSymRefAppend 按 MLIR 口径重加)
nb: str = _MlirGvSbName
p = _MlirGvReadSymName(p, nb, 512)
_MlirGvEmitAddressOf(nb)
elif string.strncmp(p, "bitcast", 7) == 0 \
or string.strncmp(p, "getelementptr", 13) == 0:
# 常量表达式整体视为符号引用(bitcast 不透明指针下恒等;GEP 取首元素地址)
nb2: str = _MlirGvSbName
p = _MlirGvScanConstExprSym(p, nb2, 512)
_MlirGvEmitAddressOf(nb2)
elif string.strncmp(p, "null", 4) == 0:
_MlirGvEmitZero(ty)
p = p + 4
elif string.strncmp(p, "zeroinitializer", 15) == 0:
_MlirGvEmitZero(ty)
p = p + 15
else:
c0: int = p[0]
if c0 == '-' or (c0 >= '0' and c0 <= '9') or c0 == '.':
vb: str = _MlirGvSbVal
p = _mlir_read_value(p, vb, 512)
_MlirGvEmitConst(vb, ty)
else:
# 不支持的叶子形态: 带原始文本响亮报错,绝不静默产错
emb: str = _MlirGvPool.alloc(string.strlen(p) + 128)
if emb is not None:
viperlib.snprintf(emb, string.strlen(p) + 128,
"MLIR 渲染: 全局初值叶子形态 v1 不支持:'%s'", p)
MlirFail(emb)
MlirFail("MLIR 渲染: 全局初值叶子形态 v1 不支持")
if depth > 0:
_MlirGvInsertLeaf(path)
return p
def _MlirGvScanConstExprSym(p: str, out: str, outsz: sizet) -> str:
"""扫描 `bitcast (…)` / `getelementptr … (…)`: 在括号组内提取首个 '@' 符号名
(剥引号写入 out),返回括号组之后的游标"""
q: str = p
while q[0] != '\0' and q[0] != '(':
q = q + 1
if q[0] != '(':
MlirFail("MLIR 渲染: 常量表达式缺少 '('(bitcast/getelementptr)")
dep: int = 0
at: str = None
guard: int = 0
while q[0] != '\0':
guard += 1
if guard > 4096:
MlirFail("MLIR 渲染: 常量表达式过长")
ch: int = q[0]
if ch == '(':
dep += 1
elif ch == ')':
dep -= 1
if dep == 0:
q = q + 1
break
elif ch == '@' and at is None:
at = q
q = q + 1
if at is None:
MlirFail("MLIR 渲染: 常量表达式中未找到符号引用 '@'")
_MlirGvReadSymName(at, out, outsz)
return q
def _MlirGvReadSymName(at: str, out: str, outsz: sizet) -> str:
"""从 '@' 处读符号名,剥掉 LLVM 口径引号("…"),返回名字之后的游标"""
if at is None or at[0] != '@':
MlirFail("MLIR 渲染: 期望符号引用('@')")
q: str = at + 1
n: sizet = 0
if q[0] == '"':
q = q + 1
while q[0] != '\0' and q[0] != '"':
if n + 1 < outsz:
out[n] = q[0]
n += 1
q = q + 1
if q[0] == '"':
q = q + 1
else:
while q[0] != '\0' and _MlirGvSymNameEnd(q[0]) == 0:
if n + 1 < outsz:
out[n] = q[0]
n += 1
q = q + 1
out[n] = '\0'
if n == 0:
MlirFail("MLIR 渲染: 空符号名")
return q
def _MlirGvSymNameEnd(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 == '}':
return 1
return 0
def _MlirGvNextLeafName() -> str:
"""分配下一个叶子 SSA 名(%v0, %v1, …;复用共享名缓冲,调用后立即取用)"""
global _MlirGvLeafSeq
ln: str = _MlirGvNameBuf
viperlib.snprintf(ln, 32, "%%v%d", _MlirGvLeafSeq)
_MlirGvLeafSeq += 1
return ln
def _MlirGvEmitAddressOf(name: str):
"""发射叶子 op: %vN = llvm.mlir.addressof @"name" : !llvm.ptr"""
ln: str = _MlirGvNextLeafName()
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " ")
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, ln)
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " = llvm.mlir.addressof ")
_MlirSymRefAppend(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, name)
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " : !llvm.ptr\n")
string.strcpy(_MlirGvLast, ln)
def _MlirGvEmitZero(ty: rawptr[LLVMType]):
"""发射叶子 op: %vN = llvm.mlir.zero : <元素 MLIR 类型>"""
mt: str = _MlirGvTyBuf
mt[0] = '\0'
TypePrint(mt, 4096, ty, _MlirGvPool)
ln: str = _MlirGvNextLeafName()
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " ")
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, ln)
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " = llvm.mlir.zero : ")
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, mt)
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, "\n")
string.strcpy(_MlirGvLast, ln)
def _MlirGvEmitConst(val: str, ty: rawptr[LLVMType]):
"""发射叶子 op: %vN = llvm.mlir.constant(<lit> : <元素 MLIR 类型>) : <同类型>"""
mt: str = _MlirGvTyBuf
mt[0] = '\0'
TypePrint(mt, 4096, ty, _MlirGvPool)
if mt[0] != 'i' and mt[0] != 'f':
MlirFail("MLIR 渲染: 全局初值标量元素类型既非整数也非浮点")
ln: str = _MlirGvNextLeafName()
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " ")
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, ln)
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " = llvm.mlir.constant(")
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, val)
# i1 的 `true`/`false` 是 MLIR 的**裸布尔关键字**,不能再带类型后缀
# (`llvm.mlir.constant(false : i1)` 报 "expected ')'");其余标量必须写
# `值 : 类型`(不写会被当成无类型 attr,与结果类型不符)。
if string.strcmp(val, "true") != 0 and string.strcmp(val, "false") != 0:
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " : ")
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, mt)
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, ") : ")
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, mt)
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, "\n")
string.strcpy(_MlirGvLast, ln)
def _MlirGvInsertLeaf(path: str):
"""发射 insertvalue(多下标路径;类型后缀始终是顶层聚合类型)"""
global _MlirGvInsSeq
_MlirGvInsSeq += 1
sn: str = _MlirGvNameBuf
viperlib.snprintf(sn, 32, "%%s%d", _MlirGvInsSeq)
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, " ")
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, sn)
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, " = llvm.insertvalue ")
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, _MlirGvLast)
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, ", ")
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, _MlirGvPrev)
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, "[")
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, path)
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, "] : ")
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, _MlirGvTopType)
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, "\n")
string.strcpy(_MlirGvPrev, sn)
string.strcpy(_MlirGvLast, sn)
def _MlirAppendIndented(dst: str, dst_size: sizet, src: str,
indent: int, pool: memhub.MemBuddy*):
"""把 src 的每一行前加 indent 个空格后追加到 dst(模块成员缩进)"""
if dst is None or src is None:
return
cb: str = pool.alloc(2)
if cb is None:
_append_cstr(dst, dst_size, src)
return
p: str = src
while p[0] != '\0':
for i in range(indent):
_append_cstr(dst, dst_size, " ")
while p[0] != '\0' and p[0] != '\n':
cb[0] = p[0]
cb[1] = '\0'
_append_cstr(dst, dst_size, cb)
p = p + 1
if p[0] == '\n':
_append_cstr(dst, dst_size, "\n")
p = p + 1
# ============================================================
# 内部辅助
# ============================================================
# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位,
# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。
# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。
_g_last_dst: uint64 = 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, _MlirModOutTrunc
if dst is None or src is None: return
dlen: sizet = 0
if _g_last_dst == uint64(dst):
dlen = _g_last_pos
# 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部
if dlen > 0 and dst[0] == '\0':
dlen = 0
# 缓存一致性校验:缓存结尾必须仍是当前文本末端(末端 NUL 且前一字符非 NUL)。
# 多个模块(__module/__function 各自一份 _append_cstr 缓存)轮流写同一 buf、
# 或 pool 复用地址残留旧内容时,缓存 pos 会过期 → 追加落到首个 NUL 之后,
# 内容"看不见"(LLvmLiteTest Test5/9 模块函数链为空根因)。校验失败回退
# strlen 重定位;正常热路径不受影响(仍 O(1))。
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 = uint64(dst)
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
if dlen >= dst_size:
_MlirModOutTrunc = 1
return
slen: sizet = string.strlen(src)
remain: sizet = dst_size - dlen
if remain <= 0:
_MlirModOutTrunc = 1
return
i: sizet = 0
while i < slen and i + 1 < remain:
dst[dlen + i] = src[i]
i += 1
if i < slen:
_MlirModOutTrunc = 1
dst[dlen + i] = '\0'
_g_last_pos = dlen + i