整个TableGen模块基于ODS(Operation Definition Specification,操作定义规范)来生成代码,TableGen:TableGen 是一套“声明式描述语言 + 由 LLVM 提供的代码生成工具链”。ODS:MLIR 利用 TableGen 定义 Operation 的一套规则/DSL。它们解决的问题是:不用手写大量重复的 C++ IR 类、解析器、打印器、验证器代码,而是用.td文件描述,然后自动生成。我们来看一个创建方言的例子,Ops.td文件的位置:mlir/examples/toy/Ch6/include/toy/Ops.td,通过一下4个步骤构建方言:定义一个xxx方言:def Toy_Dialect : Dialect { let name = "toy"; let cppNamespace = "::mlir::toy"; }这段代码用 TableGen 语法定义了一个名为 Toy 的 MLIR 方言(Dialect),逐行解释:def Toy_Dialect : Dialect { ... }def :TableGen 关键字,定义一个 具体的实例 (与 class 定义模板不同)Toy_Dialect :实例名称,后续代码通过这个名字引用该方言: Dialect :继承自 DialectBase.td 中的 class Dialect 模板let name = "toy";设置方言的 命名空间名称 为 "toy"这意味着该方言下的操作在 MLIR 文本表示中会以 toy. 为前缀,例如: toy.constant 、 toy.addlet cppNamespace = "::mlir::toy";设置生成的 C++ 代码中,该方言的操作和类型所在的 C++ 命名空间 为 ::mlir::toy生成的 C++ 类(如 ConstantOp )会被放在 namespace mlir { namespace toy { ... } } 中创建xxx方言的运算基础类class Toy_Opstring mnemonic, listTrait traits = [] : OpToy_Dialect, mnemonic, traits;class Toy_Op...class :TableGen 关键字,定义一个 模板类 (不是具体实例, def 才是实例)Toy_Op :模板名称,后续用 def 定义具体操作时继承它 模板参数 string mnemonic, listTrait traits = []参数名参数类型默认值参数含义mnemonicstring无(必填)操作的助记符,即操作名(不含方言前缀)traitslistTrait[]操作的特征列表,如 Pure、SameOperandsAndResultType 等: OpToy_Dialect, mnemonic, traits继承自 OpBase.td 中定义的 class OpDialect, string, listTraitToy_Dialect :将方言固定为 Toy 方言,这样所有继承 Toy_Op 的操作自动属于 Toy 方言mnemonic 和 traits :透传给 Op 基类作用:简化操作定义没有 Toy_Op 时 ,每个操作都要写完整的 OpToy_Dialect, ... :def ConstantOp : OpToy_Dialect, "constant", [Pure] { ... } def AddOp : OpToy_Dialect, "add", [Pure] { ... } def MulOp : OpToy_Dialect, "mul", [Pure] { ... }有了 Toy_Op 后 ,方言参数被固定,只需写助记符和特征:def ConstantOp : Toy_Op"constant", [Pure] { ... } def AddOp : Toy_Op"add", [Pure] { ... } def MulOp : Toy_Op"mul", [Pure] { ... }创建xxx方言的各种运算常量运算def ConstantOp : Toy_Op"constant", [Pure] { // Provide a summary and description for this operation. This can be used to // auto-generate documentation of the operations within our dialect. let summary = "constant"; let description = [{ Constant operation turns a literal into an SSA value. The data is attached to the operation as an attribute. For example: ```mlir %0 = toy.constant dense[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]] : tensor2x3xf64 ``` }]; // The constant operation takes an attribute as the only input. let arguments = (ins F64ElementsAttr:$value); // The constant operation returns a single value of TensorType. let results = (outs F64Tensor); // Indicate that the operation has a custom parser and printer method. let hasCustomAssemblyFormat = 1; // Add custom build methods for the constant operation. These method populates // the `state` that MLIR uses to create operations, i.e. these are used when // using `builder.createConstantOp(...)`. let builders = [ // Build a constant with a given constant tensor value. OpBuilder(ins "DenseElementsAttr":$value), [{ build($_builder, $_state, value.getType(), value); }], // Build a constant with a given constant floating-point value. OpBuilder(ins "double":$value) ]; // Indicate that additional verification for this operation is necessary. let hasVerifier = 1; }1. 操作声明def ConstantOp : Toy_Op"constant", [Pure]部分含义def ConstantOp定义一个具体操作(Operation),名称为ConstantOpToy_Op"constant", ...继承自Toy_Op,并指定该操作的助记符(mnemonic)为"constant"[Pure]特征(Trait):表示该操作是纯操作(无副作用,可以被死代码消除等优化)在 MLIR 文本中表示为: toy.constant。2.文档信息let summary = "constant"; let description = [{ Constant operation turns a literal into an SSA value. The data is attached to the operation as an attribute. For example: ```mlir %0 = toy.constant dense[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]] : tensor2x3xf64 ``` }];summary :一行摘要,用于自动生成文档description :详细描述,包含 MLIR 示例代码3. 输入参数(arguments)let arguments = (ins F64ElementsAttr:$value);部分含义(ins ...)ins标记这是输入参数(operand/参数)列表F64ElementsAttr参数类型:64 位浮点元素属性(Attribute,不是Value):$value参数名称为value关键点 :常量操作的"输入"不是运行时的值(Value),而是编译期常量属性(Attribute)。生成的 C++ 代码会有 DenseElementsAttr value() 访问器。4. 输出结果(results)let results = (outs F64Tensor);部分含义(outs ...)outs标记这是输出结果(result)列表F64Tensor结果类型:64 位浮点张量(Tensor 类型)5. 自定义汇编格式let hasCustomAssemblyFormat = 1;表示该操作有 自定义的解析和打印方法 ,不使用自动生成的格式。需要在 C++ 中手动实现 parse 和 print 方法。6. 自定义构建器(builders)let builders = [ // 构建器1:接受 DenseElementsAttr OpBuilder(ins "DenseElementsAttr":$value), [{ build($_builder, $_state, value.getType(), value); }], // 构建器2:接受 double OpBuilder(ins "double":$value) ];构建器参数作用第 1 个DenseElementsAttr value从张量属性(DenseElementsAttr)创建常量,内部调用另一个build方法第 2 个double value从单个浮点数创建常量$_builder :代表 OpBuilder $_state :代表 OperationState 这些构建器让用户可以通过 builder.createConstantOp(...) 以不同方式创建操作。针对两种类型的关系图解如下:用户代码 Builder arguments (IR 存储) ───────── ──────── ──────────────────── builder.createConstantOp(loc, 3.14) │ ▼ Builder2: double → DenseElementsAttr ← 自动转换 │ ▼ 默认 Builder: (Type, DenseElementsAttr) │ ▼ F64ElementsAttr:$value ← 最终存入 Operation builder.createConstantOp(loc, attr) │ ▼ Builder1: DenseElementsAttr → (Typ
MLIR专题1:创建方言流程(使用ODS)
发布时间:2026/6/6 21:24:26
整个TableGen模块基于ODS(Operation Definition Specification,操作定义规范)来生成代码,TableGen:TableGen 是一套“声明式描述语言 + 由 LLVM 提供的代码生成工具链”。ODS:MLIR 利用 TableGen 定义 Operation 的一套规则/DSL。它们解决的问题是:不用手写大量重复的 C++ IR 类、解析器、打印器、验证器代码,而是用.td文件描述,然后自动生成。我们来看一个创建方言的例子,Ops.td文件的位置:mlir/examples/toy/Ch6/include/toy/Ops.td,通过一下4个步骤构建方言:定义一个xxx方言:def Toy_Dialect : Dialect { let name = "toy"; let cppNamespace = "::mlir::toy"; }这段代码用 TableGen 语法定义了一个名为 Toy 的 MLIR 方言(Dialect),逐行解释:def Toy_Dialect : Dialect { ... }def :TableGen 关键字,定义一个 具体的实例 (与 class 定义模板不同)Toy_Dialect :实例名称,后续代码通过这个名字引用该方言: Dialect :继承自 DialectBase.td 中的 class Dialect 模板let name = "toy";设置方言的 命名空间名称 为 "toy"这意味着该方言下的操作在 MLIR 文本表示中会以 toy. 为前缀,例如: toy.constant 、 toy.addlet cppNamespace = "::mlir::toy";设置生成的 C++ 代码中,该方言的操作和类型所在的 C++ 命名空间 为 ::mlir::toy生成的 C++ 类(如 ConstantOp )会被放在 namespace mlir { namespace toy { ... } } 中创建xxx方言的运算基础类class Toy_Opstring mnemonic, listTrait traits = [] : OpToy_Dialect, mnemonic, traits;class Toy_Op...class :TableGen 关键字,定义一个 模板类 (不是具体实例, def 才是实例)Toy_Op :模板名称,后续用 def 定义具体操作时继承它 模板参数 string mnemonic, listTrait traits = []参数名参数类型默认值参数含义mnemonicstring无(必填)操作的助记符,即操作名(不含方言前缀)traitslistTrait[]操作的特征列表,如 Pure、SameOperandsAndResultType 等: OpToy_Dialect, mnemonic, traits继承自 OpBase.td 中定义的 class OpDialect, string, listTraitToy_Dialect :将方言固定为 Toy 方言,这样所有继承 Toy_Op 的操作自动属于 Toy 方言mnemonic 和 traits :透传给 Op 基类作用:简化操作定义没有 Toy_Op 时 ,每个操作都要写完整的 OpToy_Dialect, ... :def ConstantOp : OpToy_Dialect, "constant", [Pure] { ... } def AddOp : OpToy_Dialect, "add", [Pure] { ... } def MulOp : OpToy_Dialect, "mul", [Pure] { ... }有了 Toy_Op 后 ,方言参数被固定,只需写助记符和特征:def ConstantOp : Toy_Op"constant", [Pure] { ... } def AddOp : Toy_Op"add", [Pure] { ... } def MulOp : Toy_Op"mul", [Pure] { ... }创建xxx方言的各种运算常量运算def ConstantOp : Toy_Op"constant", [Pure] { // Provide a summary and description for this operation. This can be used to // auto-generate documentation of the operations within our dialect. let summary = "constant"; let description = [{ Constant operation turns a literal into an SSA value. The data is attached to the operation as an attribute. For example: ```mlir %0 = toy.constant dense[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]] : tensor2x3xf64 ``` }]; // The constant operation takes an attribute as the only input. let arguments = (ins F64ElementsAttr:$value); // The constant operation returns a single value of TensorType. let results = (outs F64Tensor); // Indicate that the operation has a custom parser and printer method. let hasCustomAssemblyFormat = 1; // Add custom build methods for the constant operation. These method populates // the `state` that MLIR uses to create operations, i.e. these are used when // using `builder.createConstantOp(...)`. let builders = [ // Build a constant with a given constant tensor value. OpBuilder(ins "DenseElementsAttr":$value), [{ build($_builder, $_state, value.getType(), value); }], // Build a constant with a given constant floating-point value. OpBuilder(ins "double":$value) ]; // Indicate that additional verification for this operation is necessary. let hasVerifier = 1; }1. 操作声明def ConstantOp : Toy_Op"constant", [Pure]部分含义def ConstantOp定义一个具体操作(Operation),名称为ConstantOpToy_Op"constant", ...继承自Toy_Op,并指定该操作的助记符(mnemonic)为"constant"[Pure]特征(Trait):表示该操作是纯操作(无副作用,可以被死代码消除等优化)在 MLIR 文本中表示为: toy.constant。2.文档信息let summary = "constant"; let description = [{ Constant operation turns a literal into an SSA value. The data is attached to the operation as an attribute. For example: ```mlir %0 = toy.constant dense[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]] : tensor2x3xf64 ``` }];summary :一行摘要,用于自动生成文档description :详细描述,包含 MLIR 示例代码3. 输入参数(arguments)let arguments = (ins F64ElementsAttr:$value);部分含义(ins ...)ins标记这是输入参数(operand/参数)列表F64ElementsAttr参数类型:64 位浮点元素属性(Attribute,不是Value):$value参数名称为value关键点 :常量操作的"输入"不是运行时的值(Value),而是编译期常量属性(Attribute)。生成的 C++ 代码会有 DenseElementsAttr value() 访问器。4. 输出结果(results)let results = (outs F64Tensor);部分含义(outs ...)outs标记这是输出结果(result)列表F64Tensor结果类型:64 位浮点张量(Tensor 类型)5. 自定义汇编格式let hasCustomAssemblyFormat = 1;表示该操作有 自定义的解析和打印方法 ,不使用自动生成的格式。需要在 C++ 中手动实现 parse 和 print 方法。6. 自定义构建器(builders)let builders = [ // 构建器1:接受 DenseElementsAttr OpBuilder(ins "DenseElementsAttr":$value), [{ build($_builder, $_state, value.getType(), value); }], // 构建器2:接受 double OpBuilder(ins "double":$value) ];构建器参数作用第 1 个DenseElementsAttr value从张量属性(DenseElementsAttr)创建常量,内部调用另一个build方法第 2 个double value从单个浮点数创建常量$_builder :代表 OpBuilder $_state :代表 OperationState 这些构建器让用户可以通过 builder.createConstantOp(...) 以不同方式创建操作。针对两种类型的关系图解如下:用户代码 Builder arguments (IR 存储) ───────── ──────── ──────────────────── builder.createConstantOp(loc, 3.14) │ ▼ Builder2: double → DenseElementsAttr ← 自动转换 │ ▼ 默认 Builder: (Type, DenseElementsAttr) │ ▼ F64ElementsAttr:$value ← 最终存入 Operation builder.createConstantOp(loc, attr) │ ▼ Builder1: DenseElementsAttr → (Typ