MLIR 学习笔记

本文介绍了MLIR的IR结构,包括Operation、Block、Region和Value等组件,以及如何通过TableGen进行操作定义规范。讨论了方言转换机制,涉及Pattern匹配、Builder、PatternRewriter和TypeConverter,以及ConversionTarget和ConversionConfig的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

IR 结构

MLIR 结构按照 mlir::Operation -> mlir::Region -> mlir::Block -> mlir::Operation 规则循环嵌套。一般情况下 mlir::ModuleOp 为最外层。

mlir::Operation

MLIR 所有操作的基础

可以视作有如下域说明方法
parent父节点Block *getBlock()
name名称OperationName getName()
location位置Location getLoc()
discardableAttrs/attrs普通属性ArrayRef<NamedAttribute> getDiscardableAttrs()
inherentAttrs/properties固有属性std::optional<Attribute> getInherentAttr(StringRef name)
operands操作数operand_range getOperands()
results返回值result_range getResults()
regions子区域MutableArrayRef<Region> getRegions()
successors后继(仅 terminator)SuccessorRange getSuccessors()
uses/users使用关系use_range getUses(); user_range getUsers()

注:

  • inherentAttrs/properties 主要由 tablegen 定义,主要包括 arguments 中的 Attr 类型参数以及部分 Trait 生成,用于与普通属性(attribute)区分。而 ArrayRef<NamedAttribute> getAttrs() 方法可以同时获得两种属性。

    // 示例
    // 使用<{...}>表示固有属性
    // 使用{...}表示普通属性
    %1 = "func.call"(%0) <{callee = @func}> {newattr = "test"} : (i32) -> i32
    
  • 使用walk函数遍历

  • 可定义 RegionKindInterface,默认情况下 RegionRegionKind::SSACFG 模式,表示存在 SSA 格式的控制流;也可以设置为 RegionKind::Graph 模式 ,表示 Region 内无控制流。

mlir::Op

tablegen 中继承 Op 时生成的基类,MLIR中的绝大多数操作都通过它来生成。可以视为 mlir::Operation 的某种封装?根据不同操作的实际需要,可以在tablegen文件中自定义。

可以视作有如下域说明方法
Operation *state操作Operation *getOperation()
Properties(实际储存在 state 中)固有属性InferredProperties &getProperties()

注:

  • mlir::Op 定义了操作 *->,因此既可以使用 op.func 访问 mlir::Op 及其子类封装的方法,也可以使用 op->func 访问 mlir::Operation 类的方法。

  • MLIR 里的模块、函数、控制流等等结构实际上都可以定义为 mlir::Op

mlir::Block

用于包裹一组 mlir::Operation

可以视作有如下域说明方法
parent父节点Region *getParent()
operations一组操作OpListType &getOperations()
arguments参数BlockArgListType getArguments()
terminator终结符操作Operation *getTerminator()
successors前驱(由 terminator 计算)SuccessorRange getSuccessors()
predecessors后继(由 terminator 计算)iterator_range<pred_iterator> getPredecessors()

注:

  • 使用 walk 函数遍历
  • llvm::Block 的定义不同,默认情况下 mlir::Block 内定义的值,可视范围仅为当前 Block 及其嵌套子结构,无法跨越 Block 边界,因此无法像 LLVM IR 中一样通过 Phi 节点实现跨 Block 的 SSA 结构,而是必须要通过参数传递的方式显示传递所有的值。

mlir::Region

用于包裹一组 mlir::Block

可以视作有如下域说明方法
blocks一组 BlockBlockListType &getBlocks()
arguments首个 Block 的参数BlockArgListType getArguments()
parent/container父节点Operation *getParentOp()
CFG调试用void viewGraph()

注:

  • 使用 walk 函数遍历

mlir::Value

类似 llvm::Value 作用,表示值。
// TODO SSA 模式与图模式

mlir::Type

// TODO

mlir::Attribute

// TODO

操作定义规范(ODS)

使用 TableGen 自动生成 C++ 代码的方法(文件名以 .inc 为结尾),这些代码需要手动导入。

TableGen语法

与 C++ 语法类似,文件名以 .td 结尾,需要使用专门的后端编译。详细语法参考 TableGen Programmer’s Reference(需要注意,这一套规则仅供语法参考,实际的 MLIR 方言生成大多使用特殊规则,需要看特定注释,简单的建议只使用 let)

  • class 可以继承,使用如下。TableGen class 的作用类似于 C++ 中的模板,需要 def 才会实际生成 C++ 类。
    class A<Type1 t1, Type2 t2 = ""> : B<t2> {
      let x = t1; // 覆盖父类的字段
      Type3 y = ?; // 定义新的字段
      code z = [{ ... }]; // 强调该字段是代码, [{ ... }]包裹一段字符串,可以转行。
      defvar t = 0; // 定义一个局部变量(不是字段),例如 defvar t = !ne(t2, "b");
      assert condition, message; // 若条件为假,打印一条非致命错误信息
      ...
    }
    
  • def 生成对应的 C++ 类。
    def a: A<"name", "size">{
      ...
    }
    
  • dag td参数可以是有向无环图(DAG),使用 (operator arg0, arg1, argN) 表示,operator 可以是任何 def ,常用的有 ins outs region

注:

  • 在 CMakeLists.txt 中可以很方便地使用 MLIR 提供的方法生成 .inc
    • 使用 add_circt_dialect(dialect dialect_namespace) 生成六个常用文件 ${dialect}.h.inc${dialect}.cpp.inc${dialect}Types.h.inc${dialect}Types.cpp.inc${dialect}Dialect.h.inc${dialect}Dialect.cpp.inc
    • 使用 add_mlir_interface(interface) 生成两个常用文件 ${interface}.h.inc${interface}.cpp.inc

Op

定义文件 mlir/include/mlir/IR/OpBase.td ,专门用于生成 mlir::Op 派生类的模板,挑一些我觉得重要的列出

字段说明生成C++类方法
string summary简短描述
string description详细描述
dag arguments操作的参数,分操作数与属性(固有属性)两种。操作数为运行时值,属性为编译时常量每个命名参数的 getter
dag results返回值每个命名返回值的 getter
dag regions定义操作的子域每个命名域的 getter
list<OpBuilder> builders使用 mlir::OpBuilder::create 函数生成对应 Op 时,会调用 build 函数默认生成两种常用的builder,在这里可以声明其他 builder
string assemblyFormat自定义汇编格式,可通过设置 hasCustomAssemblyFormat 在 C++ 文件中自定义生成 parser 与 print 两个函数
list<Trait> traitstraits, interfaces, and constraints 都被定义在这里,似乎可以从 InterfaceMap 中看到所有的内容。有点乱还没完全搞清楚
code extraClassDeclaration额外的成员函数声明
code extraClassDefinition额外的成员函数定义

其中汇编格式查询文档参考。

Conversion 方言转换

基于 pattern-match 模式的规则转换,基于 target, patterns, config 等定义的 mlir::OperationConverter ,通常有 PartialFullAnalysis 三种模式。三者均会以先序遍历的方式遍历控制树(包括嵌套子结构),并进行相应的类型转换。

注意:

  • 遍历的顺序可能会被改变,详见下文 生成器 章节

Pattern 模式匹配

所有匹配都需要重写 matchrewrite,或重写 matchAndRewrite 函数实现。需要满足以下约束:

  • match 阶段 IR 不应发生变化
  • 所有的 IR 改变均需要通过给定的 rewriter 修改
  • 匹配分为两种模式,即字符串匹配与按照标签匹配,其中标签有任意、interfaceIDtraitID 三种
类型说明注意事项
mlir::RewritePattern虚基类
mlir::ConversionPattern方言转换的基类封装 matchAndRewrite,暴露操作数映射后的值;可添加类型转换器;
mlir::OpConversionPattern<SourceOp>针对 mlir::Op 派生类的封装依据操作名进行字符串匹配;
mlir::OpInterfaceConversionPattern<SourceOp>针对 mlir::OpInterface 派生类的封装依据 interfaceID 进行匹配
mlir::OpTraitConversionPattern<TraitType>几乎没人用?依据 TraitType 进行匹配

Builder 生成器 / PatternRewriter / ConversionPatternRewriter

类型说明
mlir::Builder生成器的基类,管理内置的类型、属性、与 affine 表达式;
mlir::OpBuilder提供基本的 IR 生成、克隆功能;内置 InsertPoint;内置 Listener,用于通知 IR 的变更
mlir::RewriterBase/mlir::PatternRewriter重写器的基类,提供大量 IR 重写方法
mlir::ConversionPatternRewriter提供大量 IR 转换的方法;内置 ConversionPatternRewriterImpl ,记录所有的 IRRewrite 过程

其中 mlir::detail::ConversionPatternRewriterImpl 常用的记录状态如下

说明
mapping记录 ValueBlockOperation 三种映射关系
rewrites记录所有 IRRewrite 过程
replacedOps记录删除/替换的操作
regionToConverter记录每个 Region 使用的类型转换器

其中 IRRewrite 分为如下几类,可能会对 matchAndRewrite 的遍历顺序有一定影响:

类型说明对遍历的影响
BlockRewrite直接反映到 IR 上,不可回退会检查 block 参数;并立刻遍历父节点
MoveOperationRewrite直接反映到 IR 上,不可回退
ModifyOperationRewrite直接反映到 IR 上,但修改前操作会记录,可回退立刻遍历修改后的节点
ReplaceOperationRewrite删除/替换均记录为 ReplaceOperationRewrite,不直接反映到 IR 上,直到调用 commit 方法才会真实修改 IR,可回退
CreateOperationRewrite直接反映到 IR,不可回退立刻遍历新生成的节点
UnresolvedMaterializationRewrite-

TypeConverter 类型转换器

ConversionTarget

ConversionConfig

### TPU 和 MLIR 的集成及其硬件加速应用 Tensor Processing Unit (TPU) 是一种专为机器学习工作负载设计的定制化 ASIC 芯片[^1]。这些芯片旨在高效执行张量运算,这是许多现代神经网络的核心操作。 MLIR(多级中间表示)是一个编译器框架,用于构建可互操作和可重用的编译工具链组件[^2]。通过提供统一且灵活的中间表示形式,MLIR 改善了不同前端语言到后端目标架构之间的转换过程。 #### 集成方式 为了使模型能够在 TPU 上运行得更快更有效率,通常会经历以下几个阶段: - **模型定义**:开发者使用 TensorFlow 或其他支持 MLIR 导出功能的框架来创建并训练自己的模型。 - **图优化**:利用 MLIR 提供的各种变换机制对计算图进行优化处理,减少冗余计算节点数量以及调整内存访问模式等措施提升性能表现[^3]。 - **代码生成**:基于经过优化后的 IR 表达式序列,借助特定于 TPU 架构的知识库自动生成高效的汇编指令或者二进制文件格式以便加载至实际设备当中去执行推理任务。 ```cpp // 假设有一个简单的卷积层实现作为例子展示如何编写针对 TPU 的内核函数 void ConvLayer(const float* input, const float* filter, int stride_h, int stride_w, int pad_top, int pad_left, float* output) { // 这里省略具体实现细节... } ``` 这种紧密耦合不仅使得开发人员可以更容易地将自己的算法移植到新的平台上测试效果;同时也促进了整个生态系统内部各部分之间更好的协作关系形成良性循环促进技术进步与发展。 #### 性能优势 当 TPUs 与 MLIR 结合起来时,在多个方面都展现出了显著的优势: - 更高的吞吐量意味着单位时间内能够完成更多次预测请求; - 较低延迟有助于实现实时应用场景下的快速响应需求; - 减少功耗对于移动终端来说尤为重要因为它直接影响电池续航时间长短。 综上所述,TPU 和 MLIR 的结合为机器学习领域带来了强大的硬件加速能力,并简化了从高级描述向底层实现转化的过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值