MLIR 学习笔记

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

  • 26
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值