dump_ir 的输出缓冲(8/16 MB)此前用 _append_cstr **静默**丢内容 —— MLIR 文本因 内联具名结构体完整字段体而明显长于 LLVM IR,一旦超出就得到被截断的半截模块, mlir-opt 只会报难以定位的语法错。 - __module.vp:_append_cstr 写入被截断时置 _MlirModOutTrunc;LLVMModulePrint 结束前检查并 MlirFail;模块缩进辅助缓冲 64 KiB → 1 MiB(tmp_cap),并把该 缓冲分配失败也改为响亮报错(否则会静默漏印全部模块成员) - __function.vp:_append_cstr 写入被截断时置 _MlirOutTrunc;FunctionPrint 三个出口(declare / 无基本块 / 正常结束)均检查并 MlirFail
1665 lines
72 KiB
Plaintext
1665 lines
72 KiB
Plaintext
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。
|
||
# ============================================================
|
||
@t.NoVTable
|
||
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)
|
||
|
||
_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 的截断检查
|
||
# 响亮报错),这里给足 1 MiB,并把分配失败也变成响亮失败(否则会静默
|
||
# 漏印全部模块成员)。
|
||
tmp_cap: sizet = 1048576
|
||
tmp: str = pool.alloc(tmp_cap)
|
||
if tmp is None:
|
||
MlirFail("MLIR 渲染: 模块缩进辅助缓冲分配失败(1 MiB)")
|
||
|
||
# 全局变量(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 字节
|
||
GV_SIZE: t.CDefine = 72
|
||
|
||
# NamedTypeNode 硬编码大小: Next(8) + Ty(8) + IsPacked(8) = 24 字节
|
||
NAMED_TYPE_NODE_SIZE: t.CDefine = 24
|
||
|
||
# DINode 硬编码大小: Next(8) + Text(8) = 16 字节(同 llvmlite)
|
||
DI_NODE_SIZE: t.CDefine = 16
|
||
|
||
# DINamedMeta 硬编码大小: Next(8) + Name(8) + Refs(8) = 24 字节(同 llvmlite)
|
||
DI_NAMED_SIZE: t.CDefine = 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
|
||
|
||
|
||
# ============================================================
|
||
# 输出模式常量
|
||
# ============================================================
|
||
OUTPUT_FULL: t.CDefine = 0 # 完整 IR(stub + text)
|
||
OUTPUT_STUB: t.CDefine = 1 # 仅声明(header + 类型定义 + declare + external global)
|
||
OUTPUT_TEXT: t.CDefine = 2 # 仅代码(define + global 带初值)
|
||
|
||
# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast)。
|
||
# 任何一条链(函数/全局/命名类型/结构体字段)的 Next 被越界写破坏成环时,
|
||
# 遍历会无限循环导致 tpv.exe 挂起(builtins.vp dump 卡死根因排查)。
|
||
# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位"
|
||
# (对齐 memhub.self_check 的 max_steps 哲学)。
|
||
IR_DUMP_CHAIN_LIMIT: t.CDefine = 100000
|
||
|
||
|
||
# ============================================================
|
||
# 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(512)
|
||
if ty_buf is not None:
|
||
ty_buf[0] = '\0'
|
||
TypePrint(ty_buf, 512, 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(512)
|
||
if ty_buf is not None:
|
||
ty_buf[0] = '\0'
|
||
TypePrint(ty_buf, 512, 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(512)
|
||
if mtb is None:
|
||
MlirFail("MLIR 渲染: 数组元素 MLIR 类型缓冲分配失败")
|
||
mtb[0] = '\0'
|
||
MlirTypeText(mtb, 512, 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。
|
||
# ============================================================
|
||
GV_REGION_BUF_SIZE: t.CDefine = 32768
|
||
|
||
# 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
|
||
|
||
|
||
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 _MlirGvLeafBuf, _MlirGvInsBuf, _MlirGvTopType, _MlirGvLast
|
||
global _MlirGvPrev, _MlirGvPool, _MlirGvLeafSeq, _MlirGvInsSeq
|
||
x: str = gv.Initializer
|
||
if x is None:
|
||
return
|
||
topbuf: str = pool.alloc(1024)
|
||
leafbuf: str = pool.alloc(GV_REGION_BUF_SIZE)
|
||
insbuf: str = pool.alloc(GV_REGION_BUF_SIZE)
|
||
lastbuf: str = pool.alloc(32)
|
||
prevbuf: str = pool.alloc(32)
|
||
if topbuf is None or leafbuf is None or insbuf is None \
|
||
or lastbuf is None or prevbuf is None:
|
||
MlirFail("MLIR 渲染: 全局 region 缓冲分配失败")
|
||
topbuf[0] = '\0'
|
||
leafbuf[0] = '\0'
|
||
insbuf[0] = '\0'
|
||
lastbuf[0] = '\0'
|
||
prevbuf[0] = '\0'
|
||
TypePrint(topbuf, 1024, gv.Ty, pool)
|
||
|
||
_MlirGvLeafBuf = leafbuf
|
||
_MlirGvInsBuf = insbuf
|
||
_MlirGvTopType = topbuf
|
||
_MlirGvLast = lastbuf
|
||
_MlirGvPrev = prevbuf
|
||
_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 = _MlirGvPool.alloc(512)
|
||
if tb is None:
|
||
MlirFail("MLIR 渲染: 数组类型前缀缓冲分配失败")
|
||
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 = _MlirGvPool.alloc(512)
|
||
if stb is None:
|
||
MlirFail("MLIR 渲染: 结构体类型前缀缓冲分配失败")
|
||
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 = _MlirGvPool.alloc(512)
|
||
if mtb is None:
|
||
MlirFail("MLIR 渲染: MLIR 类型前缀缓冲分配失败")
|
||
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) -> str:
|
||
"""由父下标路径与子序号拼出子下标路径(顶层 path="" → "0")"""
|
||
nb: str = _MlirGvPool.alloc(string.strlen(path) + 16)
|
||
if nb is None:
|
||
MlirFail("MLIR 渲染: 下标路径缓冲分配失败")
|
||
if path[0] == '\0':
|
||
viperlib.snprintf(nb, string.strlen(path) + 16, "%d", idx)
|
||
else:
|
||
viperlib.snprintf(nb, string.strlen(path) + 16, "%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 + 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 + 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 = _MlirGvPool.alloc(512)
|
||
if tb is None:
|
||
MlirFail("MLIR 渲染: 叶子类型记号缓冲分配失败")
|
||
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 = _MlirGvPool.alloc(512)
|
||
if nb is None:
|
||
MlirFail("MLIR 渲染: 符号名缓冲分配失败")
|
||
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 = _MlirGvPool.alloc(512)
|
||
if nb2 is None:
|
||
MlirFail("MLIR 渲染: 常量表达式符号名缓冲分配失败")
|
||
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 = _MlirGvPool.alloc(512)
|
||
if vb is None:
|
||
MlirFail("MLIR 渲染: 标量初值缓冲分配失败")
|
||
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 = _MlirGvPool.alloc(32)
|
||
if ln is None:
|
||
MlirFail("MLIR 渲染: 叶子 SSA 名缓冲分配失败")
|
||
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 = _MlirGvPool.alloc(512)
|
||
if mt is None:
|
||
MlirFail("MLIR 渲染: 零初值类型缓冲分配失败")
|
||
mt[0] = '\0'
|
||
TypePrint(mt, 512, 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 = _MlirGvPool.alloc(512)
|
||
if mt is None:
|
||
MlirFail("MLIR 渲染: 标量元素类型缓冲分配失败")
|
||
mt[0] = '\0'
|
||
TypePrint(mt, 512, 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)
|
||
_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 = _MlirGvPool.alloc(32)
|
||
if sn is None:
|
||
MlirFail("MLIR 渲染: insertvalue SSA 名缓冲分配失败")
|
||
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':
|
||
i: int = 0
|
||
while i < indent:
|
||
_append_cstr(dst, dst_size, " ")
|
||
i += 1
|
||
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: 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, _MlirModOutTrunc
|
||
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)。
|
||
# 多个模块(__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 = t.CUInt64T(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 |