news 2026/11/4 7:19:49

5. MLIR相关语法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
5. MLIR相关语法

系列文章目录

章节1:什么是MLIR

章节2:MLIR的引入

章节3:MLIR完成一个端到端的流程

章节4:编写一个pass

章节5:MLIR相关语法

目录

系列文章目录

一、TableGen中的Trait

核心区别对比

1. 使用方式:混入 vs 实例化

2. 生命周期:编译期 vs 运行时

3. 存在形式:必须依附于 ConcreteType

4. 方法调用:框架调度 vs 显式调用

5. 设计目的:声明 + 注入 vs 封装

6. 组合性:可叠加

一个类比

一句话总结

二、TableGen中def的作用

1. def 的基本作用

2. 在 MLIR ODS 中,def 具体定义什么?

三、operation类和op类

核心区别:运行时容器 vs. 编译期包装

它们如何协同工作?

一个直观的例子

总结

总结



一、TableGen中的Trait

从 C++ 语法层面看,MLIR 的 Trait就是一个类模板,和普通类没有本质区别——它也能有成员变量、成员函数、静态函数、继承关系。但从设计意图和使用范式上看,它和普通类有几点关键区别。

📊 核心区别对比

维度普通类MLIR Trait
使用方式实例化对象,通过对象调用作为基类混入到 Op/Attribute/Type 中
生命周期运行时存在对象实例编译期生效,不独立实例化
存在形式可以独立存在必须依附于某个ConcreteType
方法调用显式调用成员方法被宿主继承;静态钩子由框架自动调度
设计目的封装数据和行为声明约束 + 注入通用实现
组合性单继承为主一个 Op 可叠加多个 Trait

1. 使用方式:混入 vs 实例化

普通类你会new一个对象或用栈对象:

cpp

MyClass obj; // 实例化 obj.doSomething(); // 通过对象调用

Trait从不被单独实例化。它的用法是作为模板参数“混入”到宿主类型里:

cpp

class MyOp : public Op<MyOp, MyTrait> { ... }; // 这里没有 MyTrait 的对象,只有 MyOp 的对象

MyTrait的代码通过继承成为MyOp的一部分,但你在运行时拿不到一个“MyTrait实例”。

2. 生命周期:编译期 vs 运行时

普通类的方法在运行时通过对象调用,可能有虚函数动态分发。

Trait 的成员方法在编译期就通过继承成为宿主的一部分,调用是静态解析的,没有虚表开销。Trait 的静态钩子(verifyTrait、foldTrait)也是框架在编译期就知道该调用哪个,属于静态多态。

3. 存在形式:必须依附于 ConcreteType

普通类可以独立存在。Trait 的模板参数ConcreteType意味着它必须知道“自己附着在谁身上”。一个MyTrait不能脱离具体的 Op 类型而存在——它存在的意义就是“给某个 ConcreteType 添加能力或约束”。

这也是为什么 Trait 用CRTP:基类TraitBase需要知道派生 Trait 和宿主类型,才能在编译期完成静态调度。

4. 方法调用:框架调度 vs 显式调用

普通类的方法,你不调用它就不会执行。

Trait 的静态钩子是框架按约定自动调用的:

  • verifyTrait:MLIR 验证 IR 时自动调用

  • foldTrait:常量折叠时作为回退自动调用

  • printTrait/parseTrait:自定义打印/解析时自动调用

你只要把 Trait 挂上去,这些逻辑就在合适的时机自动生效,不需要使用者手动触发。这是普通类没有的“框架回调”机制。

5. 设计目的:声明 + 注入 vs 封装

普通类的核心目的是封装数据和操作。Trait 的核心目的是:

  • 声明:告诉 MLIR 世界“这个 Op 具备什么属性”(如Commutative、Terminator)

  • 注入:把通用逻辑(验证、折叠、打印)一次性写好,自动赋给所有使用者

它更像 C++ 里的mixin或 C++20 的concept,而不是传统意义上的“对象类”。

6. 组合性:可叠加

一个 Op 可以同时附加多个 Trait:

cpp

class MyOp : public Op<MyOp, TraitA, TraitB, TraitC> { ... };

每个 Trait 各自注入自己的方法和钩子,互不冲突(同名方法需要宿主自己覆盖解决)。普通类单继承下很难做到这种灵活的横向能力组合。


一个类比

概念类比
普通类一个独立的产品,有完整的数据和行为
Trait一张能力标签/插件,贴到产品上就赋予它额外能力和约束

Trait 不是“另一种类”,而是一种用类模板实现的、面向框架回调的、编译期混入机制。语法上它是类,语义上它是“给宿主类型打的标签 + 附赠的代码”。

一句话总结

普通类回答“我是什么、我能做什么”;Trait 回答“宿主具备什么属性、框架该在什么时候调用我注入的逻辑”。前者是独立对象,后者是依附于宿主的编译期能力模块。

二、TableGen中def的作用

ef是 TableGen 用来“定义一条具体记录(record)”的关键字。它本身不是 C++ 的def,也不是运行时执行的东西;它的作用是在.td文件里声明一个具体实体,然后由mlir-tblgen解析并生成对应的 C++ 代码。

1.def的基本作用

TableGen 里常见两种定义:

tablegen

class MyClass<...> { ... } // 定义“模板/抽象记录” def MyRecord : MyClass<...>; // 定义“具体记录”
  • class:定义可复用的模板,通常带参数,不直接生成最终实体。

  • def:基于某个 class 或基类,定义一个具体的、有名字的记录。

例如:

tablegen

def MyOp : Op<"my.op", [Pure]> { let summary = "my op"; let arguments = (ins I32:$input); let results = (outs I32:$output); }

这里def MyOp就是在 ODS 中定义了一个名为MyOp的具体操作。

2. 在 MLIR ODS 中,def具体定义什么?

def可以定义 MLIR 中的多种实体:

def定义的对象例子作用
Operationdef AddOp : Op<"arith.addi", ...>定义一个具体算子
Attributedef MyAttr : AttrDef<...>定义一个自定义属性
Typedef MyType : TypeDef<...>定义一个自定义类型
Patterndef MyPattern : Pat<...>定义一个重写模式
Interfacedef MyInterface : OpInterface<...>定义一个操作接口
Dialectdef MyDialect : Dialect定义一个方言

def在 MLIR/TableGen 中的作用是:定义一个具体的记录(Operation、Attribute、Type、Pattern 等),给它一个唯一名字,并描述它的结构;TableGen 后端再根据这些def生成 C++ 代码。它本身不执行任何逻辑,而是声明式地描述“有什么”,把重复的 C++ 样板代码交给工具生成。

三、operation类和op类

Operation 是一个类(class);Op 是一个类模板(class template),本身不能直接实例化,必须提供模板实参,它本身几乎不持有数据,只是一个包装 Operation* 的强类型外壳,具体的操作类,比如AddOp、ConstantOp,是通过继承Op<AddOp, ...>实例化出来的类。

在MLIR中,Operation和Op是两层不同的抽象,服务于不同的场景。简单说,Operation是运行时那个“具体的东西”,而Op是你在C++代码里用来方便、安全地操作它的“类型化包装”。

核心区别:运行时容器 vs. 编译期包装

维度mlir::Operationmlir::Op<...>(及各派生类)
角色运行时通用容器,代表IR中一个实际存在的操作指令编译期类型安全包装,是访问Operation的“智能指针”
类型信息类型擦除,不知道自己是“加法”还是“常量”,只持有操作名(字符串)等通用信息强类型,AddOp、ConstantOp等具体类明确知道自己是什么操作
核心职责存储所有操作共有的数据:操作数、结果、属性、位置、所属区块等提供操作特定的、语义化的API,如getLhs()、getRhs()等便捷访问器
使用方式在Pass中遍历时,通常以Operation*指针形式出现按值传递和持有,内部仅包含一个Operation*指针,非常轻量

它们如何协同工作?

Operation是数据的“真身”,Op是指向它的“遥控器”。

  • Operation持有全部数据:所有操作数、结果、属性等实际数据,都存储在Operation对象内部。Op派生类自身通常不包含任何数据成员,它只是一个空壳,所有数据都委托给它所包装的那个Operation实例。

  • Op是轻量包装:mlir::Op<ConcreteType, Traits...>是一个模板类,它在编译期通过ConcreteType(如AddOp)和Traits获得类型信息,从而提供类型安全的访问方法。它的本质就是一个包装了Operation*的强类型智能指针。

一个直观的例子

在MLIR的Toy教程中,ConstantOp代表一个无输入、单输出的常量操作。它本身不存储那个常量值,值存储在它所包装的Operation对象里。ConstantOp的职责是提供getValue()这类类型安全的方法,让你方便地取出那个值。

当你从通用的Operation*得到特定的Op时,用的是dyn_cast;反之,从Op取回底层的Operation*则用getOperation():

cpp

void process(mlir::Operation *operation) { // 从通用 Operation* 转换为类型安全的 ConstantOp ConstantOp op = llvm::dyn_cast<ConstantOp>(operation); if (!op) return; // 如果不是 ConstantOp,转换失败 // 从 Op 取回底层的 Operation* mlir::Operation *internal = op.getOperation(); // internal == operation 为 true }

总结

Operation回答“IR里实际存在什么”,是通用的、类型擦除的运行时实体;Op回答“我该如何方便、安全地操作它”,是具体的、类型安全的编译期包装。在Pass中处理未知操作时用Operation*,需要调用特定操作的语义化方法时,就dyn_cast到对应的Op类


总结

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/4 7:18:56

2.倒排索引、KNN、ANN、PQ乘积量化

1.倒排索引倒排索引&#xff08;Inverted Index&#xff09;就是一张 词→文档编号 的查找表。假设你手里三张纸&#xff1a;1&#xff1a;我 爱 北京2&#xff1a;北京 好 大3&#xff1a;我 爱 吃 烤鸭 问题&#xff1a;哪张纸上写了“北京”&#xff1f;正排索引&…

作者头像 李华
网站建设 2026/11/4 7:18:19

面试官的供应链八连问:ZGC、CPU 100%、批量对账线程池、ThreadLocal 多租户坑

✍️ 作者:王文峰(外号:神秘小峯 / 山峯)|中国国学、传统文化与代码爱好者的程序人生,期待你的关注和支持! 8 道 JVM 与并发面试题:GC、排障、线程池与伪共享 一、答题方法:深挖题的三层应答 二、题 1:G1 和 ZGC 的区别?低延迟原理?大批量单据导入选哪个? 【标准答…

作者头像 李华
网站建设 2026/11/4 7:18:10

轻量化本地自动化工具 OpenClaw,Windows 与 Mac 双端搭建指南

&#x1f4cc; 说明 本文围绕 OpenClaw 展开讲解&#xff0c;全程采用图形化、可视化的交互方式。整合包已内置全部运行依赖&#xff0c;即使是普通用户&#xff0c;也能轻松复现完整的部署流程。 ✨核心亮点&#xff1a; 全程可视化图形交互界面&#xff0c;自动补齐全部运行依…

作者头像 李华
网站建设 2026/11/4 7:17:41

Eval:怎么知道你的 Agent 变好了

Eval&#xff1a;怎么知道你的 Agent 变好了 改 Agent 就像改算法 假设你在做一个推荐系统&#xff0c;某天觉得"把权重调一下&#xff0c;推荐会更准"。你改了参数&#xff0c;上线了。现在我问你&#xff1a;它真的更准了吗&#xff1f; 如果你的回答是"我觉…

作者头像 李华
网站建设 2026/11/4 7:17:40

300-500元预算,中小自动化项目PLC怎么选?避坑指南

在工业自动化圈子混久了&#xff0c;经常会接到一些让人头疼的“小活儿”&#xff1a;一个几万块的包装机改造、一套小型的恒压供水系统&#xff0c;或者车间里几台仪表的集中监控。 这种项目有个共同特点&#xff1a;预算卡得死&#xff0c;通常控制器的成本就卡在300-500元这…

作者头像 李华