assemblyFormat 是精确控制操作在文本IR中解析和打印格式的规则,决定字段显式性、顺序、分隔符、可选性及类型附着方式,不改变语义也不参与类型推导或验证。

assemblyFormat 不是用来“简化语法”的,它是用来精确控制操作在文本格式中如何被解析和打印的规则。你不能靠它省代码,但写错它会导致 parse 失败或 print 出来不可读。
assemblyFormat 决定的是文本 IR 的“长相”,不是语义
比如 toy.transpose 在 IR 中写成:%t = toy.transpose %x {inplace = true} : (tensor) -> tensor,这个结构不是硬编码在 C++ 里,而是由 assemblyFormat 字符串驱动的。它不改变操作行为,只决定:哪些字段要显式写出、顺序怎么排、用什么符号分隔、哪些可选、类型怎么附着。
- 没定义
assemblyFormat?MLIR 会 fallback 到默认的通用格式(类似op_name $operands attr-dict `:` functional-type),但往往难读、冗余、不符合领域习惯 - 写得太松?比如漏掉
type($result),那打印出来的 IR 就没返回类型,下游工具(如 verifier、lowering pass)可能直接报错 - 写得太死?比如把
$lhs和$rhs强制写成固定顺序,但操作本身是可交换的(如arith.addi),就会让 IR 反直觉
常见 assemblyFormat 片段含义与易错点
assemblyFormat 是 TableGen 字符串,不是正则也不是模板引擎。每个 token 都有固定含义:
-
$lhs,$rhs,$inputs,$result:对应 ODS 中声明的参数名,大小写敏感,拼错就解析失败 -
attr-dict:必须显式写出,否则属性(如{inplace = true})不会出现在文本中;若想跳过空字典,得加(attr-dict)? -
`:`和`->`:反引号包裹的字面量,冒号和箭头是普通字符,不是语法糖;少一个反引号,TableGen 生成直接报错 -
type($inputs):自动展开为所有输入类型的元组,但如果$inputs是空列表,这里会生成(),而某些方言期望省略整个type(...)段——得用条件语法(`:` type($inputs))?
如何调试 assemblyFormat 是否生效
最直接的办法是写个最小 .td 文件,跑 mlir-tblgen 看生成的 parse / print 函数有没有按预期调用字段访问器:
- 在生成的
.cpp文件里搜parse,看是否调用了parseOperand、parseAttribute等;没调说明assemblyFormat语法错误或字段名不匹配 - 手动构造一条 IR(比如
%0 = mydialect.foo %a : i32),用mlir-opt --verify-diagnostics测试 parse 是否成功;失败时错误信息通常指向某字段缺失或类型不匹配,而不是 assemblyFormat 本身 - 用
mlir-translate --mlir-to-llvmir或直接mlir-opt --print-op-generic对比“格式化后”和“泛型格式”输出,确认assemblyFormat真正在控制外观
真正容易被忽略的点是:assemblyFormat 不参与类型推导、不触发验证逻辑、也不影响 operation 构造过程。它只是 parse/print 这两个边界环节的“皮肤”。一旦 IR 进入内存,值就只是 SSA value,跟你怎么写的 assemblyFormat 没关系了。















