├── .clangd
├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── ex1-io
├── CMakeLists.txt
├── ex1-io.cpp
└── ex1.mlir
├── ex2-build-new
├── CMakeLists.txt
├── cgeist-input.c
├── cgeist-output.mlir
├── cmake-cmd.sh
└── toy-build.cpp
├── ex2-build
├── CMakeLists.txt
└── ex2-build.cpp
├── ex3-dialect
├── CMakeLists.txt
├── ex3-cse.mlir
├── ex3.mlir
├── include
│ └── toy
│ │ ├── CMakeLists.txt
│ │ ├── Toy.td
│ │ ├── ToyDialect.h
│ │ ├── ToyDialect.td
│ │ ├── ToyOps.h
│ │ └── ToyOps.td
├── lib
│ ├── CMakeLists.txt
│ └── toy.cpp
└── tools
│ └── toy-opt
│ ├── CMakeLists.txt
│ └── toy-opt.cpp
├── ex4-beautiful-dialect
├── CMakeLists.txt
├── ex4.mlir
├── include
│ └── toy
│ │ ├── CMakeLists.txt
│ │ ├── Toy.td
│ │ ├── ToyDialect.h
│ │ ├── ToyDialect.td
│ │ ├── ToyOps.h
│ │ └── ToyOps.td
├── lib
│ ├── CMakeLists.txt
│ └── toy.cpp
└── tools
│ └── toy-opt
│ ├── CMakeLists.txt
│ └── toy-opt.cpp
├── ex5-pass-new
├── CMakeLists.txt
├── cmake-cmd.sh
├── include
│ ├── CMakeLists.txt
│ └── toy
│ │ ├── CMakeLists.txt
│ │ └── Passes
│ │ ├── CMakeLists.txt
│ │ ├── Passes.h
│ │ └── Passes.td
├── lib
│ ├── CMakeLists.txt
│ └── toy
│ │ ├── CMakeLists.txt
│ │ └── Passes
│ │ ├── CMakeLists.txt
│ │ ├── PassDetails.h
│ │ ├── ToyIterArgs.cpp
│ │ └── ToyLoopUnroll.cpp
├── sumArray.mlir
└── tools
│ ├── CMakeLists.txt
│ └── toy-opt
│ ├── CMakeLists.txt
│ └── toy-opt.cpp
├── ex5-pass
├── CMakeLists.txt
├── ex5.mlir
├── include
│ └── toy
│ │ ├── CMakeLists.txt
│ │ ├── Toy.td
│ │ ├── ToyDialect.h
│ │ ├── ToyDialect.td
│ │ ├── ToyOps.h
│ │ ├── ToyOps.td
│ │ ├── ToyPasses.h
│ │ └── ToyPasses.td
├── lib
│ ├── CMakeLists.txt
│ ├── Transforms
│ │ ├── CMakeLists.txt
│ │ ├── ConvertToyToArith.cpp
│ │ └── DCE.cpp
│ └── toy.cpp
└── tools
│ └── toy-opt
│ ├── CMakeLists.txt
│ └── toy-opt.cpp
├── ex6-pattern
├── CMakeLists.txt
├── ex6.mlir
├── include
│ └── toy
│ │ ├── CMakeLists.txt
│ │ ├── Toy.td
│ │ ├── ToyDialect.h
│ │ ├── ToyDialect.td
│ │ ├── ToyOps.h
│ │ ├── ToyOps.td
│ │ ├── ToyPasses.h
│ │ └── ToyPasses.td
├── lib
│ ├── CMakeLists.txt
│ ├── Transforms
│ │ ├── CMakeLists.txt
│ │ ├── ConvertToyToArith.cpp
│ │ └── DCE.cpp
│ └── toy.cpp
└── tools
│ └── toy-opt
│ ├── CMakeLists.txt
│ └── toy-opt.cpp
├── ex7-convert
├── CMakeLists.txt
├── ex7.mlir
├── include
│ └── toy
│ │ ├── CMakeLists.txt
│ │ ├── Toy.td
│ │ ├── ToyDialect.h
│ │ ├── ToyDialect.td
│ │ ├── ToyOps.h
│ │ ├── ToyOps.td
│ │ ├── ToyPasses.h
│ │ ├── ToyPasses.td
│ │ ├── ToyTypes.h
│ │ └── ToyTypes.td
├── lib
│ ├── CMakeLists.txt
│ ├── Transforms
│ │ ├── CMakeLists.txt
│ │ ├── ConvertToyToArith.cpp
│ │ └── DCE.cpp
│ └── toy.cpp
└── tools
│ └── toy-opt
│ ├── CMakeLists.txt
│ └── toy-opt.cpp
└── fig
├── .gitignore
├── DefUseChains.svg
├── MLIR Dialects.jpg
├── Use-list.svg
├── op.drawio
└── op.svg
/.clangd:
--------------------------------------------------------------------------------
1 | CompileFlags:
2 | Remove:
3 | - -fno-lifetime-dse
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.cache
2 | /build
3 | /.vscode
4 | **/build
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.13.4)
2 |
3 | project(mlir-toy VERSION 0.0.0)
4 |
5 | # 防止 cmake 生成一堆 warning
6 | cmake_policy(SET CMP0116 NEW)
7 |
8 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
9 | set(CMAKE_CXX_STANDARD 17)
10 | set(CMAKE_CXX_STANDARD_REQUIRED YES)
11 |
12 | # 设置输出目录
13 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
14 |
15 | add_compile_options(-g)
16 |
17 | find_package(MLIR REQUIRED CONFIG)
18 | list(APPEND CMAKE_MODULE_PATH "${MLIR_CMAKE_DIR}")
19 | list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
20 |
21 | include(TableGen)
22 | include(AddLLVM)
23 | include(AddMLIR)
24 | include(HandleLLVMOptions)
25 |
26 | include_directories(${LLVM_INCLUDE_DIRS} ${MLIR_INCLUDE_DIRS})
27 |
28 | add_subdirectory(ex1-io)
29 | add_subdirectory(ex2-build)
30 | add_subdirectory(ex3-dialect)
31 | add_subdirectory(ex4-beautiful-dialect)
32 | add_subdirectory(ex5-pass)
33 | add_subdirectory(ex7-convert)
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Hands-On Practical MLIR Tutorial
2 |
3 | Kexing Zhou(周可行)
4 |
5 | 北京大学
6 |
7 | zhoukexing@pku.edu.cn
8 |
9 |
10 | * 1. [MLIR 简介](#mlir-简介)
11 | * 1.1. [MLIR 编译管线](#mlir-编译管线)
12 | * 1.2. [常见的 Dialect](#常见的-dialect)
13 | * 1.3. [insight:“及时做优化”](#insight:“及时做优化”)
14 | * 1.4. [MLIR 的用处](#mlir-的用处)
15 | * 1.5. [MLIR 的缺点](#mlir-的缺点)
16 | * 2. [MLIR 基本用法](#mlir-基本用法)
17 | * 2.1. [IR 基本结构](#ir-基本结构)
18 | * 2.2. [MLIR 基本工程模板](#mlir-基本工程模板)
19 | * 2.2.1. [配置 clangd](#配置-clangd)
20 | * 2.3. [MLIR 的读入、输出](#mlir-的读入、输出)
21 | * 2.4. [用代码生成 MLIR](#用代码生成-mlir)
22 | * 3. [MLIR Op 的结构](#mlir-op-的结构)
23 | * 3.1. [Attribute 和 Operand](#attribute-和-operand)
24 | * 3.2. [Attribute, Value 和 Type](#attribute,-value-和-type)
25 | * 4. [MLIR 的类型转换](#mlir-的类型转换)
26 | * 4.1. [Op 的类型转换](#op-的类型转换)
27 | * 4.2. [Type / Attribute 的类型转换](#type-/-attribute-的类型转换)
28 | * 5. [MLIR 的图结构](#mlir-的图结构)
29 | * 5.1. [MLIR 数据流图结构](#mlir-数据流图结构)
30 | * 5.2. [MLIR 数据流图的遍历与修改](#mlir-数据流图的遍历与修改)
31 | * 5.3. [MLIR 控制流图的遍历与修改](#mlir-控制流图的遍历与修改)
32 | * 6. [基本的 Dialect 工程](#基本的-dialect-工程)
33 | * 6.1. [TableGen 工程模板](#tablegen-工程模板)
34 | * 6.2. [Tablegen Language Server](#tablegen-language-server)
35 | * 6.3. [IR 的默认定义与实现](#ir-的默认定义与实现)
36 | * 6.3.1. [TableGen 文件](#tablegen-文件)
37 | * 6.3.2. [头文件](#头文件)
38 | * 6.3.3. [库文件](#库文件)
39 | * 6.3.4. [程序入口](#程序入口)
40 | * 7. [TableGen Op 定义详解](#tablegen-op-定义详解)
41 | * 7.1. [Attribute、Type、Constraint](#attribute、type、constraint)
42 | * 7.1.1. [内置 Attribute](#内置-attribute)
43 | * 7.1.2. [内置的 Type](#内置的-type)
44 | * 7.1.3. [为什么 Attribute 和 Type 都是 Constraint](#为什么-attribute-和-type-都是-constraint)
45 | * 7.2. [Verifier:发现IR错误](#verifier:发现ir错误)
46 | * 7.2.1. [emitError](#emiterror)
47 | * 7.2.2. [LogicalResult](#logicalresult)
48 | * 7.3. [Variadic:可变参数](#variadic:可变参数)
49 | * 7.3.1. [多个可变参数:AttrSizedOperandSegments](#多个可变参数:attrsizedoperandsegments)
50 | * 7.4. [AssemblyFormat:更易读的输出](#assemblyformat:更易读的输出)
51 | * 7.4.1. [常用关键字](#常用关键字)
52 | * 7.4.2. [额外 attr 字典](#额外-attr-字典)
53 | * 7.4.3. [输出 type](#输出-type)
54 | * 7.4.4. [可选输出:Optional、UnitAttr](#可选输出:optional、unitattr)
55 | * 7.5. [Builder:自定义 create 函数](#builder:自定义-create-函数)
56 | * 7.5.1. [默认Builder](#默认builder)
57 | * 7.5.2. [自定义builder](#自定义builder)
58 | * 7.6. [自定义函数](#自定义函数)
59 | * 7.6.1. [header target](#header-target)
60 | * 7.7. [使用 Trait](#使用-trait)
61 | * 7.7.1. [内存副作用:SideEffectInterfaces](#内存副作用:sideeffectinterfaces)
62 | * 7.7.2. [类型推断:InferTypeOpInterface](#类型推断:infertypeopinterface)
63 | * 7.8. [函数:FunctionOpTrait](#函数:functionoptrait)
64 | * 7.8.1. [定义 Return](#定义-return)
65 | * 7.8.2. [定义 Function](#定义-function)
66 | * 7.8.3. [定义 Call](#定义-call)
67 | * 8. [添加 Pass](#添加-pass)
68 | * 8.1. [Pass 工程模板](#pass-工程模板)
69 | * 8.2. [Pass 定义详解](#pass-定义详解)
70 | * 8.2.1. [指定 Pass 在哪个 Op 上运行](#指定-pass-在哪个-op-上运行)
71 | * 8.2.2. [带参数的 Pass](#带参数的-pass)
72 | * 8.3. [简单的 DCE Pass 实现](#简单的-dce-pass-实现)
73 | * 8.3.1. [定义](#定义)
74 | * 8.3.2. [实现](#实现)
75 | * 9. [Pattern Rewrite](#pattern-rewrite)
76 | * 9.1. [Pattern Rewrite](#pattern-rewrite-1)
77 | * 9.1.1. [描述 Pattern](#描述-pattern)
78 | * 9.1.2. [调用 Pattern](#调用-pattern)
79 | * 9.1.3. [Depedent Dialect & Linking](#depedent-dialect-&-linking)
80 | * 9.2. [Dialect Convertion (Type Conversion)](#dialect-convertion-(type-conversion))
81 | * 9.2.1. [TypeConverter](#typeconverter)
82 | * 9.2.2. [Conversion Pattern:自动做 Operand 的类型转换](#conversion-pattern:自动做-operand-的类型转换)
83 | * 9.2.3. [类型转换的细节与 Debug](#类型转换的细节与-debug)
84 | * 9.2.4. [使用自己的 materialization](#使用自己的-materialization)
85 | * 9.3. [使用 MLIR 里已有的 Pattern 做多步转换](#使用-mlir-里已有的-pattern-做多步转换)
86 | * 10. [自定义 Type](#自定义-type)
87 | * 11. [TIPS](#tips)
88 | * 11.1. [如何找头文件、找想要的函数](#如何找头文件、找想要的函数)
89 | * 11.2. [如何找需要链接的库](#如何找需要链接的库)
90 | * 11.3. [如何加快编译速度](#如何加快编译速度)
91 | * 11.4. [去 MLIR 里抄代码](#去-mlir-里抄代码)
92 | * 12. [MLIR 的批判:C++ v.s. Rust](#mlir-的批判:c++-v.s.-rust)
93 | * 13. [Issue & Reply](#issue-&-reply)
94 |
95 |
99 |
100 |
101 |
102 | ## 1. MLIR 简介
103 |
104 | ### 1.1. MLIR 编译管线
105 |
106 | MLIR 在于设计一套可复用的编译管线,包括可复用的 IR、Pass 和 IO 系统。在 IR 中,多个 Dialect 可以混合存在。MLIR 已经定义好了一套 Dialect Translation Graph:
107 |
108 | 
109 |
110 | ### 1.2. 常见的 Dialect
111 |
112 | MLIR 的 Dialect 是相对独立的,下面列举一些常见的 dialect:
113 |
114 | 1. **func**:处理函数的dialect,包含的函数定义、调用、返回等基本操作
115 | 2. **arith**:处理加减乘除移位等各种运算
116 | * **math**:更复杂的运算,如 log, exp, tan 等
117 | 3. **affine**:处理循环嵌套,实现了循环展开、多面体变换等一些算法
118 | 4. **scf**:(Structured Control Flow) 结构化控制流,保留 for,if 等语句
119 | * **cf**:无结构控制流,只有条件跳转命令
120 | 5. **llvm**:LLVM IR 的 binding,可以直接翻译给 LLVM 做后续编译
121 |
122 | MLIR的编译从高层次的 tensor 到 低层次的 scf,cf,每个阶段都是多个 dialect 的混合体,每次 lowering 往往只针对一个 dialect 进行。
123 |
124 |
163 |
164 | ### 1.3. insight:“及时做优化”
165 |
166 | 这里简单举例,dialect 是如何混合的。
167 |
168 | 例如,我是 pytorch,生成了一些神经网络,我想要表示这些运算:
169 |
170 | * Tensor 是一块带 shape 的指针:使用 **tensor** dialect
171 | * 简单的 elementwise 加减乘除:使用 **arith** dialect
172 | * 复杂的 log、exp 等运算:使用 **math** dialect
173 | * 矩阵线性代数运算:使用 **linalg** dialect
174 | * 可能有一些控制流:使用 **scf** dialect
175 | * 整个网络是一个函数:使用 **func** dialect
176 |
177 | 接下来,将其逐渐 lower 到 LLVM:
178 |
179 | * 调用 Lowering Pass,把 **tensor** lowering 到 **linalg**,而其他的 dialect 不会改变。
180 | * 继续调用 pass,直到把 **linalg** 转换到 **affine** -> **scf** -> **cf**,其他表示运算的 dialect 保留不变。
181 | * 继续 Lowering,把 **memref** 转换为裸指针、**arith** 和 **func** 转换为 llvm 内置运算。
182 | * 最后,所有非 **llvm** dialect 都被转换为了 **llvm** dialect,现在可以导出为 llvm ir 交给 llvm 继续编译。
183 |
184 | **可见,MLIR 编译有一个特点**:不同 dialect 是独立的。
185 |
186 | * 例如,在做循环展开等优化的时候,我不需要关心加法和减法可以合并;而在做算数表达式优化的时候,也不需要关心当前在哪个函数里边。
187 |
188 |
189 | **MLIR 可以从各个层次优化 IR**:例如:
190 |
191 | * 在 **affine** 层面,可以根据循环大小做展开,向量化
192 | * 在 **scf** 层面,可以发现循环不变量
193 | * 在 **arith** 层面,可以用算数恒等式优化代码
194 |
195 | MLIR 的 insight 在于“**及时做优化**”。很明显,linalg 层次,我们很容易发现矩阵被转置了两次,但一旦 lower 到 scf,所有转置操作都变成循环,优化就很难进行了。
196 |
197 | ### 1.4. MLIR 的用处
198 |
199 | 我们使用 MLIR,主要也是想要复用别人已经写好的代码,一般包括:
200 |
201 | * 复用已有 dialect 作为 **输入**,不用自己写前端。
202 | * 如 [Polygeist](https://github.com/llvm/Polygeist) 能把 C 翻译成 Affine Dialect,这样我们就不用写 C Parser
203 | * 将已有 dialect **混入**或**作为输出**。
204 | * 如 arith 等 dialect,可以直接集成起来,不需要自己写。
205 | * 要生成 binary 的时候,可以直接生成 LLVM Dialect,复用后端 LLVM 编译管线
206 | * 复用已有的 Pass。
207 | * 常见的 Pass 如 CSE,DCE 可以复用
208 | * Dialect 专用 Pass,如循环展开,也可以复用
209 |
210 | ### 1.5. MLIR 的缺点
211 |
212 | MLIR 也有缺点:
213 |
214 | * 太过笨重,编译、链接时间长(可能会链接出上百M的文件)
215 | * 可以用 lld 来加快链接速度,但依然很慢 [见TIPS](#113-如何加快编译速度)
216 | * Dialect 定义极不灵活,定义较复杂 Op 时非常麻烦
217 |
218 | ## 2. MLIR 基本用法
219 |
220 | ### 2.1. IR 基本结构
221 |
222 | MLIR 是 树形结构,每个节点是 Operation,Op 可以组成 Block,Block 组成 Region,而 Region 又可以嵌套在 Op 内部。
223 |
224 | * **Operation** 指单个运算,运算内可以嵌套 **Region**
225 | * **Block** 指基本块,基本块包含一个或多个 **Operation**
226 | * **Region** 指区域,类似于循环体或函数体,包含若干 **Block**
227 |
228 | MLIR 的基本块使用 **“基本块参数”** 来取代“phi函数”,如下面的例子:
229 |
230 | * **块参数**:每个基本块都带有参数,块内可以使用
231 | * **终止符**:每个基本块的一般为跳转或返回,跳转时需要附上块参数
232 |
233 | ```mlir
234 | module {
235 | func.func @foo(%a: i32, %b: i32, %c: i32) -> i32 {
236 | %cmp = arith.cmpi "sge", %a, %b : i32
237 | cf.cond_br %cmp, ^add(%a: i32), ^add(%b: i32)
238 | ^add(%1: i32):
239 | %ret = llvm.add %1, %c : i32
240 | cf.br ^ret
241 | ^ret:
242 | func.return %ret : i32
243 | }
244 | }
245 | ```
246 |
247 | **module**: 默认情况下,mlir 最外层是 `builtin.module`,作为 IR 的根。
248 |
249 | ### 2.2. MLIR 基本工程模板
250 |
251 | 构建第一个 mlir 项目往往非常困难,下面给一个我常用的工程模板:
252 |
253 | ```
254 | mlir-tutorial
255 | ├── install # Install Prefix,把 MLIR 编译后安装到这里
256 | ├── llvm-project # MLIR 项目
257 | └── mlir-toy # 自己的 MLIR 工程
258 | ```
259 |
260 | 首先,按照 MLIR [getting started](https://mlir.llvm.org/getting_started/) 的方法,安装 MLIR。注意,安装的时候要设置 PREFIX 为 install 目录,如下面所示,和 getting start 上的略有区别:
261 |
262 | ```bash
263 | git clone https://github.com/llvm/llvm-project.git
264 | cd llvm-project
265 | git checkout 26eb4285b56edd8c897642078d91f16ff0fd3472 # 本教程使用的版本,Polygeist Oct 14, 2023依赖版本
266 | mkdir build && cd build
267 | cmake -G Ninja ../llvm \
268 | -DCMAKE_INSTALL_PREFIX=/mlir-tutorial/install \
269 | -DLLVM_ENABLE_PROJECTS=mlir \
270 | -DLLVM_BUILD_EXAMPLES=ON \
271 | -DLLVM_TARGETS_TO_BUILD="Native;NVPTX;AMDGPU" \
272 | -DCMAKE_BUILD_TYPE=Release \
273 | -DLLVM_ENABLE_ASSERTIONS=ON
274 | ```
275 |
276 | 在 build 完之后,安装到 prefix
277 |
278 | ```bash
279 | ninja install
280 | ```
281 |
282 | 现在,mlir 把所有二进制文件、库文件都装到了 install 目录下。现在可以 `export PATH=/mlir-tutorial/install/bin:$PATH`,方便调用 `bin` 里面的 `mlir-opt` 等程序。
283 |
284 | ```bash
285 | install
286 | ├── bin
287 | ├── examples
288 | ├── include
289 | ├── lib
290 | └── share
291 | ```
292 |
293 | 接下来,在 mlir-toy 里面建立一个简单的工程
294 |
295 | ```bash
296 | mlir-toy
297 | ├── CMakeLists.txt
298 | └── main.cpp
299 | ```
300 |
301 | 其中 CMakeLists.txt 文件写法比较固定:
302 |
303 | ```cmake
304 | cmake_minimum_required(VERSION 3.13.4)
305 |
306 | project(mlir-toy VERSION 0.0.0)
307 |
308 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # 生成 compile_commands.json 便于代码高亮
309 | set(CMAKE_CXX_STANDARD 17)
310 | set(CMAKE_CXX_STANDARD_REQUIRED YES)
311 |
312 | find_package(MLIR REQUIRED CONFIG)
313 | list(APPEND CMAKE_MODULE_PATH "${MLIR_CMAKE_DIR}")
314 | list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
315 |
316 | include(TableGen)
317 | include(AddLLVM)
318 | include(AddMLIR)
319 | include(HandleLLVMOptions)
320 |
321 | include_directories(${LLVM_INCLUDE_DIRS} ${MLIR_INCLUDE_DIRS})
322 |
323 | add_executable(mlir-toy main.cpp)
324 | ```
325 |
326 | 在 main.cpp 里面写一个 main 函数,然后先 build 一下。注意,必须要写上 `CMAKE_INSTALL_PREFIX`,这样cmake 可以自动找到 MLIR。
327 |
328 | ```bash
329 | cd build
330 | cmake .. -GNinja -DCMAKE_INSTALL_PREFIX=/mlir-tutorial/install
331 | ninja
332 | ```
333 |
334 | #### 2.2.1. 配置 clangd
335 |
336 | 使用 vscode 默认的 lint 工具跑 mlir 会非常卡,建议使用 clangd。
337 |
338 | * 在扩展里安装 clangd 插件
339 | * cmake 的时候加上 `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`(上面的CMakeLists.txt 已经加上了)
340 | * 有时候要吧 compile_commands.json 拷贝到工程根目录,或者在 vscode 设置里配置一下
341 | * 一旦发现高亮炸了,vscode里 Ctrl + Shift + P,输入 clangd: restart language server
342 | * 有时候,mlir 的编译选项与 clangd 冲突,在 mlir-toy 目录下建立 .clangd 文件,去掉相关的选项:
343 | ```yaml
344 | CompileFlags:
345 | Remove:
346 | - -fno-lifetime-dse
347 | ```
348 |
349 | ### 2.3. MLIR 的读入、输出
350 |
351 | 测试用 mlir:
352 |
353 | ```mlir
354 | func.func @test(%a: i32, %b: i32) -> i32 {
355 | %c = arith.addi %a, %b : i32
356 | func.return %c : i32
357 | }
358 | ```
359 |
360 | 最简单的读入输出:
361 |
362 | ```cpp
363 | #include "mlir/IR/AsmState.h"
364 | #include "mlir/IR/BuiltinOps.h"
365 | #include "mlir/IR/MLIRContext.h"
366 | #include "mlir/Parser/Parser.h"
367 | #include "mlir/Support/FileUtilities.h"
368 | #include "mlir/Dialect/Func/IR/FuncOps.h"
369 | #include "mlir/Dialect/Arith/IR/Arith.h"
370 | #include "llvm/Support/raw_ostream.h"
371 |
372 | using namespace mlir;
373 |
374 | int main(int argc, char ** argv) {
375 | MLIRContext ctx;
376 | // 首先,注册需要的 dialect
377 | ctx.loadDialect();
378 | // 读入文件
379 | auto src = parseSourceFile(argv[1], &ctx);
380 | // 输出dialect,也可以输出到 llvm::errs(), llvm::dbgs()
381 | src->print(llvm::outs());
382 | // 简单的输出,在 debug 的时候常用
383 | src->dump();
384 | return 0;
385 | }
386 | ```
387 |
388 | 需要链接上所有依赖的文件:
389 |
390 | ```cmake
391 | target_link_libraries(
392 | ex1-io
393 | MLIRIR
394 | MLIRParser
395 | MLIRFuncDialect
396 | MLIRArithDialect
397 | )
398 | ```
399 |
400 | 测试方式:
401 |
402 | ```bash
403 | ./ex1-io ../ex1-io/ex1.mlir
404 | ```
405 |
406 | ### 2.4. 用代码生成 MLIR
407 |
408 | ```cpp
409 | #include "mlir/IR/AsmState.h"
410 | #include "mlir/IR/Builders.h"
411 | #include "mlir/IR/BuiltinOps.h"
412 | #include "mlir/IR/MLIRContext.h"
413 | #include "mlir/Parser/Parser.h"
414 | #include "mlir/Support/FileUtilities.h"
415 |
416 | #include "mlir/Dialect/Func/IR/FuncOps.h"
417 | #include "mlir/Dialect/Arith/IR/Arith.h"
418 | #include "llvm/Support/raw_ostream.h"
419 |
420 | using namespace mlir;
421 |
422 | int main(int argc, char ** argv) {
423 | MLIRContext ctx;
424 | ctx.loadDialect();
425 |
426 | // 创建 OpBuilder
427 | OpBuilder builder(&ctx);
428 | auto mod = builder.create(builder.getUnknownLoc());
429 |
430 | // 设置插入点
431 | builder.setInsertionPointToEnd(mod.getBody());
432 |
433 | // 创建 func
434 | auto i32 = builder.getI32Type();
435 | auto funcType = builder.getFunctionType({i32, i32}, {i32});
436 | auto func = builder.create(builder.getUnknownLoc(), "test", funcType);
437 |
438 | // 添加基本块
439 | auto entry = func.addEntryBlock();
440 | auto args = entry->getArguments();
441 |
442 | // 设置插入点
443 | builder.setInsertionPointToEnd(entry);
444 |
445 | // 创建 arith.addi
446 | auto addi = builder.create(builder.getUnknownLoc(), args[0], args[1]);
447 |
448 | // 创建 func.return
449 | builder.create(builder.getUnknownLoc(), ValueRange({addi}));
450 | mod->print(llvm::outs());
451 | return 0;
452 | }
453 | ```
454 |
455 | **如何寻找 builder.create 的参数**:builder.create 内部是调用 `Op::build` 函数的,你可以 Ctrl + 鼠标点击找到 `func::FuncOp` 的定义,然后找里面的 build 函数,看参数表。
456 |
457 | ## 3. MLIR Op 的结构
458 |
459 | MLIR 的一个 Operation 里可以包含下面的一些东西:
460 |
461 | * Operand:这个 Op 接受的操作数
462 | * Result:这个 Op 生成的新 Value
463 | * Attribute:可以理解为编译器常量
464 | * Region:这个 Op 内部的 Region
465 |
466 | MLIR 中,Attribute 是高度灵活的,允许插入原来不存在的 attr,允许不同 dialect 互相插入 attribute。
467 |
468 | ### 3.1. Attribute 和 Operand
469 |
470 | Attribute 和 Operand 有一些区别。Attribute 指的编译器已知的量,而 Operand 指只有运行时才能知道的量。
471 |
472 | 如下面的这个Op,0 是一个 Attribute 而不是一个 Operand
473 |
474 | ```mlir
475 | %c0 = arith.constant 0 : i32
476 | ```
477 |
478 | ### 3.2. Attribute, Value 和 Type
479 |
480 | Value 必然包含 Type,Type 也可以作为 Attribute 附加在 Operation 上。
481 |
482 | 例如函数 Op,虽然 %a, %b 出现在了参数表里,但它们实际上是函数类型的一部分,算是 Type Attribute。
483 |
484 | ```mlir
485 | func.func @test(%a: i32, %b: i32) -> i32 {
486 | %c = arith.addi %a, %b : i32
487 | func.return %c : i32
488 | }
489 | ```
490 |
491 | * `mlir-opt --mlir-print-op-generic` 来打印这里的代码,得到下面的代码。参数名被隐去,只有 function_type 作为 attribute 保留了下来。
492 | ```mlir
493 | "builtin.module"() ({
494 | "func.func"() <{function_type = (i32, i32) -> i32, sym_name = "test"}> ({
495 | ^bb0(%arg0: i32, %arg1: i32):
496 | %0 = "arith.addi"(%arg0, %arg1) : (i32, i32) -> i32
497 | "func.return"(%0) : (i32) -> ()
498 | }) : () -> ()
499 | }) : () -> ()
500 | ```
501 |
502 | ## 4. MLIR 的类型转换
503 |
504 | ### 4.1. Op 的类型转换
505 |
506 | MLIR 的所有 Op 都有一个统一的储存格式,叫 `Operation`。`Operation` 里面存了 OpName 和所有的 operands, results, attributes 和其它的东西。
507 |
508 | 用户定义的 `arith.addi` 等等 Op,本质上都是 `Operation` 的指针。但与 `Operation*` 不同的是,`AddIOp` 定义了 `Operation` 里储存的数据的解释方式。如 AddOp,自己是一个 `Operation` 的指针,也定义了一个函数 `getLhs` 用来返回第一个值,当作 lhs。
509 |
510 |
511 |
512 | **DownCast**:如何在拿到 `Operation*` 的情况下,将其转换为 `AddOp` 呢?llvm 提供了一些转换函数,这些函数会检查 Operation 的 OpName,并进行转换。
513 |
514 | ```cpp
515 | using namespace llvm;
516 | void myCast(Operation * op) {
517 | auto res = cast(op); // 直接转换,失败报错
518 | auto res = dyn_cast(op); // 尝试转换,失败返回 null,op为null时报错
519 | auto res = dyn_cast_if_present(op); // 类似 dyn_cast,op为null时返回null
520 | }
521 | ```
522 |
523 | **相等关系**:两个 Operation* 相等,指的是它们指向同一个 Operation 实例,而不是这个 Operation 的 operand,result,attr 相等。
524 |
525 | **Hashing**:在不修改 IR 的情况下,每个 `Operation` 有唯一地址。于是,可以直接用 `Operation*` 当作值建立哈系表,用来统计 IR 中数据或做分析:
526 |
527 | ```cpp
528 | #include "llvm/ADT/DenseMap.h"
529 | llvm::DenseMap numberOfReference;
530 | ```
531 |
532 | ### 4.2. Type / Attribute 的类型转换
533 |
534 | MLIR 的 Type 和 Attribute 与 Op 类似。Type 是到 TypeStorage 的指针,Attribute 也是到 AttributeStorage 的指针。
535 |
536 | * TypeStorage 里面会存 Type 的参数,如 Integer 会存 width,Array 会存 Shape。
537 |
538 | | 专用指针 | 通用指针 | 值(存在 Context中)|
539 | |:------------|:------------|:--------------------|
540 | | AddOp | Operation* | Operation |
541 | | IntegerType | Type | TypeStorage |
542 | | IntegerAttr | Attribute | AttrStorage |
543 |
544 | **全局单例**:与 Op 不同的是,MLIR Context 会完成 Type 和 Attribute 的去重工作。**Type相等,它们的TypeStorage也一定相等。**
545 |
546 | **DownCast**:Type 的 DownCast 与 Op 相同。
547 |
548 | **Hashing**:与 Op 类似,Type 也可以作为 Key 来建哈系表,但不那么常用。
549 |
550 | ## 5. MLIR 的图结构
551 |
552 | MLIR 里,有两个层次的图:
553 |
554 | * 第一个是 Region 嵌套构成的树,这个图表示 **控制流**
555 | * 第二个是 Op/Value 构成的图,这个图表示 **数据流**
556 |
557 | ### 5.1. MLIR 数据流图结构
558 |
559 | MLIR 的数据流图是由 Operation 和 Value 构成的。MLIR 官网上,IR Structure 里面的 [两幅图](https://mlir.llvm.org/docs/Tutorials/UnderstandingTheIRStructure/#traversing-the-def-use-chains) 将 MLIR 的图结构解释得非常清楚:
560 |
561 | 首先是 **Operation 的连接**:
562 |
563 | * Value 要么来自于 Operation 的 Result 要么来自于 BlockArgument
564 | * 每个 Operation 的 Operand 都是到 Value 的指针
565 | * 要修改 Operand 的时候,实际修改的应该是 OpOperand
566 |
567 |
568 |
569 | 然后,是 **Value 的 use-chain**:
570 |
571 | * 每个 Value 都将其 User 连接在一起了
572 |
573 |
574 |
575 | 可见,MLIR 的图是一个双向的图结构,在遍历尤其是修改的时候需要特别小心。
576 |
577 | * 在修改 OpOpeand 的时候,对应 value 的 use-chain 会暗中被 MLIR 改掉
578 | * 在调用 `value->getDefiningOp()` 的时候,BlockArgument 会返回 null
579 |
580 | ### 5.2. MLIR 数据流图的遍历与修改
581 |
582 | MLIR 数据流图的遍历往往遵循一种模式:Operation 调用函数找 Value,再用 Value 调用函数找 Operation,交替进行。
583 |
584 | 其中,**Operation 找 Value 的方法**有:
585 |
586 | * **getOperands**、**getResults**:这两个非常常用,如下面的代码可以用来 Op 找 Op
587 | ```cpp
588 | for(auto operand: op->getOperands()) {
589 | if(auto def = operand.getDefiningOp()) {
590 | // do something
591 | }
592 | else {
593 | // block argument
594 | }
595 | }
596 | ```
597 | * **getOpOperands**:这个在需要更改 operands 的时候非常有用,例如下面的代码将 value 做替换:
598 | ```cpp
599 | IRMapping mapping;
600 | // 将 op1 的 results 映射到 op2 的 results
601 | mapping.map(op1->getResults(), op2->getResults());
602 | for(auto &opOperand: op3->getOpOperands()) {
603 | // 将 op3 的参数里含有 op1 results 的替换为 op2 的
604 | // lookupOrDefault 指找不到 mapping 就用原来的
605 | opOperand.set(mapping.lookupOrDefault(opOperand.get()));
606 | }
607 | ```
608 |
609 | **Value 找 Op 的方法**有:
610 |
611 | * **getDefiningOp**:可能返回 null
612 | * **getUses**:返回 OpOperand 的迭代器
613 | * **getUsers**:返回 Operation 的迭代器
614 |
615 | **Op的getUses和getUser**:operation 也有 getUses 和 getUsers 函数,等价于把这个 op 的所有 result 的 Uses 或 Users 拼在一起。
616 |
617 | **Value的修改**:Value 支持 **replaceAllUsesWith** 修改,一种*看起来*等价的代码是:
618 | ```cpp
619 | for(auto & uses: value.getUses()) {
620 | uses.set(new_value);
621 | }
622 | ```
623 | 但需要注意,上面的代码是**非常危险**的。因为在 uses.set 的时候,会修改 value 的 use chain,而 value 的 use-chain 正在被遍历,可能一修改就挂了。于是,最好用 mlir 提供好的 `replaceAllUsesWith` 来修改。
624 |
625 | ### 5.3. MLIR 控制流图的遍历与修改
626 |
627 | 与数据流图相比,控制流图遍历更简单,常用的一些函数:
628 |
629 | * **op.getParentOp**, **op.getParentOfType**:获取父亲Op
630 | * **op.getBlock**:注意是返回父亲block,而不是函数block
631 | * **op.getBody**:这个才是返回内部 block / region
632 |
633 | 遍历儿子的方法:
634 |
635 | * **op.walk**:递归地遍历所有子孙op:
636 |
637 | ```cpp
638 | // 递归遍历所有儿子
639 | func.walk([](Operation * child) {
640 | // do something
641 | });
642 | // 递归遍历所有是 `ReturnOp` 类型的儿子
643 | func.walk([](ReturnOp ret) {
644 | // do something
645 | })
646 | ```
647 |
648 | * **block**:直接就是一个 iterator,可以直接遍历:
649 |
650 | ```cpp
651 | Block * block = xxx
652 | for(auto & item: *block) {
653 | // do something
654 | }
655 | ```
656 |
657 | 其他遍历方法如 `getOps` 可以自行尝试。
658 |
659 | 控制流图的修改主要用 `OpBuilder` 完成。强烈建议把找到 `OpBuilder` 的代码,把里面有什么函数都看一看,常见的:
660 |
661 | * **builder.create**:创建op
662 | * **builder.insert**:插入remove的op
663 | * **op->remove()**:从当前块移除,但不删除,可以插入到其他块内
664 | * **op->erase()**:从当前块移除,并且删除
665 |
666 | **删除顺序**:在删除一个 op 的时候,这个 op 不能存在 user,否则会报错。
667 |
668 | ## 6. 基本的 Dialect 工程
669 |
670 | 这一节会讲如何用 tablegen 定义自己的 dialect,使用 mlir 自带的通用程序入口 `MlirOptMain`,生成 `toy-opt`。
671 |
672 | 这只是一个简单的工程模板,`toy-opt` 只能识别 Op 的 generic 格式。但先不着急,我们先构建出工程的骨架,在往上不断添加 feature。
673 |
674 | ```mlir
675 | %c = "toy.add"(%a, %b): (i32, i32) -> i32 // 可以读取
676 | %c = toy.add %a, %b : i32 // 无法读取
677 | ```
678 |
679 | ### 6.1. TableGen 工程模板
680 |
681 | 这个过于复杂,请参考附带例子 `ex3-dialect`:
682 |
683 | 文件结构:
684 |
685 | ```bash
686 | ex3-dialect
687 | ├── CMakeLists.txt # 控制其他各个部分的 CMakeList
688 | ├── include
689 | │ └── toy
690 | │ ├── CMakeLists.txt # 控制 Dialect 定义的 CMakeList
691 | │ ├── ToyDialect.h # Dialect 头文件
692 | │ ├── ToyDialect.td # Dialect TableGen 文件
693 | │ ├── ToyOps.h # Op 头文件
694 | │ ├── ToyOps.td # Op TableGen 文件
695 | │ └── Toy.td # 把 ToyDialect.td 和 ToyOps.td include 到一起,用于 tablegen
696 | ├── lib
697 | │ ├── CMakeLists.txt
698 | │ └── toy.cpp # Dialect library
699 | └── tools
700 | └── toy-opt
701 | ├── CMakeLists.txt
702 | └── toy-opt.cpp # Executable Tool
703 | ```
704 |
705 | ### 6.2. Tablegen Language Server
706 |
707 | vscode 提供 mlir 扩展,可以为我们写 tablegen 文件提供帮助。在 `/mlir-tutorial/install/bin` 里面,有 `mlir-lsp-server`。在 vscode 的设置里找到 mlir-lsp-server 的设置,设好绝对路径,还有 database 的路径。
708 |
709 | 注意,lsp-server 很容易突然崩溃,炸了的时候用 Ctrl+Shift+P,"mlir: restart language server"。
710 |
711 | ### 6.3. IR 的默认定义与实现
712 |
713 | #### 6.3.1. TableGen 文件
714 |
715 | 1. `include/ToyDialect.td`:定义 Dialect 名字和cpp命名空间
716 |
717 | ```tablegen
718 | include "mlir/IR/OpBase.td"
719 | def ToyDialect : Dialect {
720 | let name = "toy";
721 | let cppNamespace = "::toy";
722 | let summary = "Toy Dialect";
723 | }
724 | ```
725 |
726 | 2. `include/ToyOps.td`:定义 Operation
727 |
728 | ```tablegen
729 | include "mlir/IR/OpBase.td"
730 | include "toy/ToyDialect.td"
731 | include "mlir/Interfaces/SideEffectInterfaces.td"
732 |
733 | // mnemonic 指名字
734 | class ToyOp traits = []> :
735 | Op;
736 |
737 | // Pure 是 Trait,表示没有 SideEffect 的纯函数
738 | def AddOp : ToyOp<"add", [Pure]> {
739 | let summary = "add operation";
740 | let arguments = (ins AnyInteger:$lhs, AnyInteger:$rhs);
741 | let results = (outs AnyInteger:$result);
742 | }
743 | ```
744 |
745 | 3. `include/Toy.td`:把其他的 td include 到一起,用于交给 tablegen 生成
746 |
747 | ```tablegen
748 | include "toy/ToyDialect.td"
749 | include "toy/ToyOps.td"
750 | ```
751 |
752 | 注意添加 include 目录。
753 | ```cmake
754 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
755 | ```
756 |
757 | 4. `include/CMakeLists.txt`:调用 tablegen 生成代码,其中,第一个 Toy 是 Dialect 的名字,第二个 toy 指的是 `toy.td`
758 |
759 | ```cmake
760 | add_mlir_dialect(Toy toy)
761 | ```
762 |
763 | #### 6.3.2. 头文件
764 |
765 | 5. tablegen 生成的文件放在 `build/include/toy` 里,包括默认的定义和实现
766 |
767 | * `ToyDialect.{h,cpp}.inc`:存 Dialect 的定义和实现
768 | * `Toy.{h,cpp}.inc`:存 Op 的定义和实现
769 |
770 | tablegen 生成到了 `build` 目录,需要额外添加 include
771 | ```cmake
772 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
773 | ```
774 |
775 | 6. `include/ToyDialect.h`:把 Dialect 的定义加载进来
776 |
777 | ```cpp
778 | #pragma once
779 | #include "mlir/IR/BuiltinDialect.h"
780 | #include "toy/ToyDialect.h.inc" // include 进来就可以了
781 | ```
782 |
783 | 7. `include/ToyOps.h`:把 Op 的定义加载进来
784 |
785 | ```cpp
786 | #pragma once
787 | #include "mlir/IR/BuiltinOps.h"
788 | #include "mlir/IR/Builders.h"
789 | // td 里面 include 的,这里也要 include 对应的 h 文件
790 | #include "toy/ToyDialect.h"
791 | #include "mlir/Interfaces/SideEffectInterfaces.h"
792 | #define GET_OP_CLASSES
793 | #include "toy/Toy.h.inc"
794 | ```
795 |
796 | #### 6.3.3. 库文件
797 |
798 | 8. `lib/toy.cpp`:把默认 Dialect 和 Op 的默认实现加载进来
799 |
800 | ```cpp
801 | #include "toy/ToyDialect.h"
802 | #include "toy/ToyOps.h"
803 | #include "toy/ToyDialect.cpp.inc"
804 | #define GET_OP_CLASSES
805 | #include "toy/Toy.cpp.inc"
806 | using namespace toy;
807 | void ToyDialect::initialize() {
808 | // 下面的代码会生成 Op 的列表,专门用来初始化
809 | addOperations<
810 | #define GET_OP_LIST
811 | #include "toy/Toy.cpp.inc"
812 | >();
813 | }
814 | ```
815 |
816 | 9. `lib/CMakeLists.txt`:前面的 tablegen 会生成一个 `MLIRxxxIncGen` 的 Target,library 需要依赖这个 Target,才能先生成头文件,再编译 toy.cpp。一般 Library 取名为 `MLIRToy` 或者 `Toy`。
817 | ```cmake
818 | add_mlir_library(Toy toy.cpp DEPENDS MLIRToyIncGen)
819 | ```
820 |
821 | #### 6.3.4. 程序入口
822 |
823 | 10. `tools/toy-opt/toy-opt.cpp`:mlir 提供了一个可复用的通用的程序入口,我们可以在 `MlirOptMain` 前面注册我们想要的 Dialect 和 Pass,接下来调用 `MlirOptMain`,就可以使用一些默认提供的功能。
824 |
825 | ```cpp
826 | #include "mlir/IR/DialectRegistry.h"
827 | #include "mlir/Tools/mlir-opt/MlirOptMain.h"
828 | // 导入 Func Dialect
829 | #include "mlir/Dialect/Func/IR/FuncOps.h"
830 | // 导入 MLIR 自带 Pass
831 | #include "mlir/Transforms/Passes.h"
832 | // 导入我们新建的 Dialect
833 | #include "toy/ToyDialect.h"
834 | using namespace mlir;
835 | using namespace llvm;
836 |
837 | int main(int argc, char ** argv) {
838 | DialectRegistry registry;
839 | // 注册 Dialect
840 | registry.insert();
841 | // 注册两个 Pass
842 | registerCSEPass();
843 | registerCanonicalizerPass();
844 | return asMainReturnCode(MlirOptMain(argc, argv, "toy-opt", registry));
845 | }
846 | ```
847 |
848 | 注意,此时需要链接上 `MLIROptLib` 库:在 `tools/toy-opt/CMakeLists.txt` 里
849 |
850 | ```cmake
851 | add_mlir_tool(toy-opt toy-opt.cpp)
852 | target_link_libraries(toy-opt
853 | PRIVATE
854 | MLIRIR MLIRParser MLIRSupport
855 | Toy # 对应 #include "toy/ToyDialect.h"
856 | MLIROptLib # 对应 #include "mlir/Tools/mlir-opt/MlirOptMain.h"
857 | MLIRFuncDialect # 对应 #include "mlir/Dialect/Func/IR/FuncOps.h"
858 | MLIRTransforms # 对应 #include "mlir/Transforms/Passes.h"
859 | )
860 | ```
861 |
862 | 11. 简单使用 `ninja toy-opt`
863 | * `./toy-opt --help` 可以打文档,里面应该有 cse 和 canonicalize 两个 pass
864 | * `./toy-opt ../ex3-dialect/ex3.mlir` 读文件
865 | * `./toy-opt -canonicalize ../ex3-dialect/ex3-cse.mlir`,可以做 dce
866 | * `./toy-opt -cse ../ex3-dialect/ex3-cse.mlir`,可以做 cse
867 |
868 | 为什么 mlir 知道我们的 Op 可以被 CSE 和 DCE 呢,因为我们给 Op 标记了 `Pure` Trait,这表示这个 Op 是纯函数。`Pure` Trait 会自动帮我们注册对应 Op 的 CSE 和 DCE 模式。
869 |
870 | ## 7. TableGen Op 定义详解
871 |
872 | 上一节介绍了 MLIR 工程的骨架,现在我们为其添砖加瓦,让 IR 的定义、输入、输出更简单。
873 |
874 | ### 7.1. Attribute、Type、Constraint
875 |
876 | 添加 Attribute 的方法与 Operand 类似,都写在 arguments 里面。
877 |
878 | ```tablegen
879 | def ConstantOp : ToyOp<"const", [Pure]> {
880 | let summary = "const operation";
881 | let arguments = (ins APIntAttr:$value);
882 | let results = (outs AnyInteger:$result);
883 | }
884 | ```
885 |
886 | #### 7.1.1. 内置 Attribute
887 |
888 | 见 `mlir/IR/CommonAttrConstraints.td`,常用的:
889 |
890 | * `TypeAttrOf`:将一个 Type 作为 Attr
891 | * `FlatSymbolRefAttr`:call 函数的时候,函数名的 Attr
892 | * `SymbolNameAttr`: 定义函数的时候,函数名的 Attr
893 | * `UnitAttr`:表示一个bool,为true的时候,它在 attr 表里面,为 false 的时候不在
894 | * `I64SmallVectorArrayAttr`:整数数组 Attr,与其他的整数数组区别的是,它用 SmallVector,会好用一些
895 |
896 | #### 7.1.2. 内置的 Type
897 |
898 | 见 `mlir/IR/CommonTypeConstraint.td`,常用的:
899 |
900 | * `I1`, `I8`, `I16`, `I32`, `I64`, `I128`
901 | * `AnyType`:表示任何类型
902 | * `AnyInteger`:表示任何整数
903 |
904 | #### 7.1.3. 为什么 Attribute 和 Type 都是 Constraint
905 |
906 | 为 Op 定义一个 Attribute 的时候,实际上是指定了 [Operation](#41-op-的类型转换) 里面 operands, results, attributes 等等 的解释方式。
907 |
908 | 像 Attribute、Type 这样的 表示了 “第 i 个位置的 operand 只能被解释为整数”、“第 j 个位置的 attr 只能被解释为Symbol” 的约定,算是限制了各个 field 的解释方式,被看作是 “Constraint”。
909 |
910 | ### 7.2. Verifier:发现IR错误
911 |
912 | 在 tablegen 里面加上 `hasVerifier=true`
913 |
914 | ```mlir
915 | def SubOp : ToyOp<"sub", [Pure]> {
916 | let summary = "sub operation";
917 | let arguments = (ins AnyInteger:$lhs, AnyInteger:$rhs);
918 | let results = (outs AnyInteger:$result);
919 | let hasVerifier = true;
920 | }
921 | ```
922 |
923 | 然后在 `toy.cpp` 里写 verifier 的实现:
924 |
925 | ```cpp
926 | using namespace mlir;
927 | LogicalResult SubOp::verify() {
928 | if (getLhs().getType() != getRhs().getType())
929 | return this->emitError() << "Lhs Type " << getLhs().getType()
930 | << " not equal to rhs " << getRhs().getType();
931 | return success();
932 | }
933 | ```
934 |
935 | #### 7.2.1. emitError
936 |
937 | emitError 是 Op 带有的函数。MLIR里面 Op 都会带 `emitError` 函数,用来区分是哪个Op发生了错误。在这里,我们 verify 的是自己,就只需要调用自己的 `emitError` 函数。
938 |
939 | * 还有 `emitWarning`,可以输出 Warning。
940 |
941 | #### 7.2.2. LogicalResult
942 |
943 | MLIR 用 LogicalResult 用来表示类似 bool 的值,它的特点是:
944 |
945 | * mlir的一些其他类型可以自动转换为 LogicalResult,如上面 emitError 就可以自动转换
946 | * 用 success(), failure() 生成 true 和 false
947 | * 用 succeed(x), failed(x) 来判读是否为 true, false
948 |
949 | ### 7.3. Variadic:可变参数
950 |
951 | 使用 `Variadic` 来描述可变参数:
952 |
953 | ```tablegen
954 | def AddOp : ToyOp<"add", [Pure]> {
955 | let summary = "add operation";
956 | let arguments = (ins Variadic:$inputs);
957 | let results = (outs AnyInteger:$result);
958 | }
959 | ```
960 |
961 | 使用 `Option` 来描述可选参数:
962 |
963 | ```tablegen
964 | def ReturnOp : ToyOp<"return", [Terminator, ReturnLike]> {
965 | let summary = "return operation"
966 | let arguments = (ins Optional:$data);
967 | }
968 | ```
969 |
970 | #### 7.3.1. 多个可变参数:AttrSizedOperandSegments
971 |
972 | 当一个函数只有一个 `Variadic` 或 `Optional` 的时候,可以根据参数总数量推断有多少个可变参数。但如果有多个 `Variadic` 或 `Optional`,需要增加 `AttrSizedOperandSegments` Trait,这个 trait 会为 Op 添加一个 attribute 用来记录每个可变参数是否存在,如果存在有多少个。
973 |
974 | 与之相关的还有 `AttrSizedResultSegments` 用于返回了多个可变参数的情况,它们都在 `OpBase.td` 里面。
975 |
976 | ### 7.4. AssemblyFormat:更易读的输出
977 |
978 | 例子:
979 |
980 | ```tablegen
981 | def AddOp : ToyOp<"add", [Pure]> {
982 | let summary = "add operation";
983 | let arguments = (ins Variadic:$inputs);
984 | let results = (outs AnyInteger:$result);
985 | let assemblyFormat = "$inputs attr-dict `:` type($inputs) `->` type($result)";
986 | }
987 | ```
988 |
989 | 这样会生成下面的更可读的代码:
990 |
991 | ```mlir
992 | %0 = toy.add %a, %b : i32, i32 -> i32
993 | ```
994 |
995 | #### 7.4.1. 常用关键字
996 |
997 | * `$xxx` 用来表示 operand 或者 attribute
998 | * `type($xxx)` 用来表示 xxx 的类型。
999 | * ``` `keyword` ```: 插入 keyword
1000 | * `functional-type($inputs, results)`,生成形如 `(i32, i32) -> i32` 的函数类型
1001 | * `attr-dict`:表示额外的 attr 字典。
1002 |
1003 | #### 7.4.2. 额外 attr 字典
1004 |
1005 | mlir 允许为 OP 插入任意的 attribute,允许跨 dialect 插入 attribute。所以,在定义 op 的时候,总是要把 `attr-dict` 加上,这样其他人插入的 attr 也能存下来。
1006 |
1007 | #### 7.4.3. 输出 type
1008 |
1009 | 所有没有限制死(AnyXXX,如 AnyInteger)的 operand,都需要写清楚 type,要么用 `type($xxx)`,要么用 `functional-type`。
1010 |
1011 | #### 7.4.4. 可选输出:Optional、UnitAttr
1012 |
1013 | 针对 Optional 和 UnitAttr,MLIR 提供了一种 条件分组 的语法:如下面的 HWReg
1014 |
1015 | ```tablegen
1016 | def HWRegOp : ToyOp<"reg"> {
1017 | let summary = "hardware register";
1018 | let arguments = (ins I1:$clock, AnyInteger:$input, Optional:$reset, UnitAttr:$is_public);
1019 | let results = (outs AnyInteger:$result);
1020 | let assemblyFormat = [{
1021 | (`public` $is_public^)? $input
1022 | `clock` $clock
1023 | (`reset` $reset^)?
1024 | attr-dict `:` functional-type($input, results)
1025 | }];
1026 | }
1027 | ```
1028 |
1029 | 可以有下面的效果:
1030 |
1031 | ```mlir
1032 | %a = toy.reg public %b clock %clk reset %reset : (i32) -> i32
1033 | %a = toy.reg %b clock %clk reset %reset : (i32) -> i32
1034 | %a = toy.reg %b clock %clk : (i32) -> i32
1035 | ```
1036 |
1037 | * `[{xxx}]`,MLIR中的长文本可以用 `[{}]` 括起来。
1038 | * ``(`reset` $reset^)?``,其中 `(...)?` 表示分组,`^` 表示判断依据。只有对应的 `Optional` 或 `UnitAttr` 存在的时候,才会输出这个分组。
1039 |
1040 | ### 7.5. Builder:自定义 create 函数
1041 |
1042 | Builder 会在 `builder.create()` 的时候被调用,一个更简单的 builder 可以让创建 Op 更快捷。
1043 |
1044 | #### 7.5.1. 默认Builder
1045 |
1046 | MLIR 会默认生成一些builder。默认 builder 会要求先传入 result 的类型,再传入 operand,attribute 的值。
1047 |
1048 | ```cpp
1049 | build(
1050 | $_builder, $_state,
1051 | mlir::Type Res1Type, mlir::Type Res2Type, ...,
1052 | mlir::Value arg1, mlir::Value arg2, ...
1053 | )
1054 | ```
1055 |
1056 | 有时候,一些常用的 Attr,如 `StringAttr`,MLIR 会自动生成以 `StringRef` 为参数的 builder,用来方便调用。用户就不需要使用 `builder.getStringAttr(xxx)` 先把 `StringRef` 转换为 `StringAttr` 再来传参数了。
1057 |
1058 | [之前](#711-内置-attribute) 提到的 `I64SmallVectorArrayAttr` 就可以直接传一个 `SmallVector`,而不需要传一个 Attr 进去,会非常方便。
1059 |
1060 | #### 7.5.2. 自定义builder
1061 |
1062 | 例如,我们可以在创建 Op 的时候,推断出结果的类型:
1063 |
1064 | ```mlir
1065 | def SubOp : ToyOp<"sub", [Pure]> {
1066 | let summary = "sub operation";
1067 | let arguments = (ins AnyInteger:$lhs, AnyInteger:$rhs);
1068 | let results = (outs AnyInteger:$result);
1069 | let builders = [
1070 | OpBuilder<
1071 | (ins "mlir::Value":$lhs, "mlir::Value":$rhs),
1072 | "build($_builder, $_state, lhs.getType(), lhs, rhs);"
1073 | >
1074 | ];
1075 | let hasVerifier = true;
1076 | }
1077 | ```
1078 |
1079 | * 首先,mlir 会自动为我们生成 `build($_builder, $_state, ResultType, LhsValue, RhsValue)` 的 builder
1080 | * 我们的 builder 通过 `lhs.getType()` 推断 result 的类型,并调用 mlir 生成好的 builder,实现自动推断类型
1081 |
1082 | 如果只是为了推断类型,建议使用 MLIR 为类型推断专门实现的 Trait: InferTypeOpInterface [后面有介绍](#772-类型推断infertypeopinterface)。
1083 |
1084 | ### 7.6. 自定义函数
1085 |
1086 | tablegen 允许用户为 Op 添加自定义函数,例如,我想直接获取 ConstantOp 的类型的位宽:
1087 |
1088 | ```tablegen
1089 | def ConstantOp : ToyOp<...> {
1090 | let extraClassDeclaration = [{
1091 | int64_t getBitWidth() {
1092 | return getResult().getType().getWidth();
1093 | }
1094 | }];
1095 | }
1096 | ```
1097 |
1098 | 这样,之后想要获取位宽的时候,就可以更简洁了:
1099 |
1100 | ```cpp
1101 | auto w = op.getResult().getType().getWidth();
1102 | auto w = op.getBitWidth();
1103 | ```
1104 |
1105 | 由于 tablegen 里面没有 cpp 的语法补全,可以只在 tablegen 里写一个方法定义,然后在 `toy.cpp` 里面写实现(使用 `ninja MLIRToyIncGen` 生成头文件)
1106 |
1107 | ```tablegen
1108 | def ConstantOp : ToyOp<...> {
1109 | let extraClassDeclaration = [{
1110 | int64_t getBitWidth();
1111 | }];
1112 | }
1113 | ```
1114 |
1115 | #### 7.6.1. header target
1116 |
1117 | 一个 trick 是在 `CMakeLists.txt` 里面添加一个 target,这样每次改了 tablegen 文件,只需要 `ninja header` 就能生成头文件。
1118 |
1119 | ```cmake
1120 | add_custom_target(header DEPENDS MLIRToyIncGen)
1121 | ```
1122 |
1123 | ### 7.7. 使用 Trait
1124 |
1125 | [前面](#634-程序入口) 介绍到,在给 Op 标记上 `Pure` 之后,就会自动被 cse, dce Pass 理解。除了 Pure Trait 之外,MLIR 为我们提供了很多好用的 Trait,这里介绍常用的 SideEffect,InferType 和 比较复杂的和函数相关的 Trait。
1126 |
1127 | 使用 Trait 的时候要注意:
1128 |
1129 | 1. Interface 可能会要求用户实现一些固定的接口,trait 里一些 `InterfaceMethod` 是没有默认实现的。
1130 | 2. 在 td 里要 include trait 的 td 文件,在 h 里也要 include 对应的 h 文件
1131 |
1132 | #### 7.7.1. 内存副作用:SideEffectInterfaces
1133 |
1134 | `mlir/Interfaces/SideEffectInterfaces.{td,h}` 文件里定义了内存副作用的 interface
1135 |
1136 | * **Pure**:纯函数,使用后可以自动 cse,dce
1137 | * `MemRead`, `MemWrite`, `MemAlloc`, `MemFree`:内存作用
1138 |
1139 | #### 7.7.2. 类型推断:InferTypeOpInterface
1140 |
1141 | `mlir/Interfaces/InferTypeOpInterface.{td,h}` 文件里定义了类型推断的 Interface,使用类型推断,你可以:
1142 |
1143 | * 在 `assemblyFormat` 里可以少写几个 type,写出来的格式更漂亮
1144 | * 自动生成带类型推断的 builder,只需要传参数就能推断返回值类型
1145 |
1146 | 类型推断主要有下面几个常用的 Trait:
1147 |
1148 | * **SameOperandsAndResultType**:操作数和返回值有相同的类型,使用后 assemblyFormat 里就只需要写任何某一个操作数的类型
1149 | * **InferTypeOpInterface**:通过输入和 attr 的类型推断返回值类型,自己写推断函数
1150 | * **InferTypeOpAdaptor**:与上一个相似,但封装了一个 Adaptor,写起来会更简单
1151 |
1152 | 推荐使用 **InferTypeOpAdaptor**:
1153 | * 在 tablegen 里面
1154 | ```tablegen
1155 | def ConstantOp : ToyOp<"const", [Pure, InferTypeOpAdaptor]> {
1156 | let summary = "const operation";
1157 | let arguments = (ins APIntAttr:$value);
1158 | let results = (outs AnyInteger:$result);
1159 | let assemblyFormat = "$value attr-dict"; // 这里不需要写 type($result) 了
1160 | }
1161 | ```
1162 | * 在 `toy.cpp` 里面
1163 | ```cpp
1164 | mlir::LogicalResult ConstantOp::inferReturnTypes(
1165 | mlir::MLIRContext * context,
1166 | std::optional location,
1167 | Adaptor adaptor,
1168 | llvm::SmallVectorImpl & inferedReturnType
1169 | ) {
1170 | // adaptor 是 “incomplete op”,表示只知道输入,不知道返回值的 Op
1171 | auto type = adaptor.getValueAttr().getType();
1172 | inferedReturnType.push_back(type);
1173 | return mlir::success();
1174 | }
1175 | ```
1176 |
1177 | ### 7.8. 函数:FunctionOpTrait
1178 |
1179 | 这里着重介绍 func、call、return,参考 `ex4-beautiful-dialect`。函数的这一套代码非常固定,每次照搬就好,没有太多的解释。按照下面的说明设置好函数Op后,应该就可以用 `./ex4-opt ../ex4-beautiful-dialect/ex4.mlir` 来读取函数了。
1180 |
1181 | #### 7.8.1. 定义 Return
1182 |
1183 | Return 是一个终止符,需要使用 `Terminator`。同时,我们为其加上 `ReturnLike`。
1184 |
1185 | ```tablegen
1186 | def ReturnOp : ToyOp<"ret", [Terminator, ReturnLike]> {
1187 | let summary = "return operation";
1188 | let arguments = (ins AnyType:$data);
1189 | let assemblyFormat = "$data attr-dict `:` type($data)";
1190 | }
1191 | ```
1192 |
1193 | #### 7.8.2. 定义 Function
1194 |
1195 | 定义函数需要实现 `FunctionOpInterface`,它依赖于 `Symbol` 和 `CallableOpInterface`。
1196 | 同时,因为我们定义了 Region,最好还加上 `RegionKindInterface`,它会自动为我们检查 Region 的格式是否正确。
1197 |
1198 | 我们的 Function 是一个全局的函数,需要加上 `IsolatedFromAbove` Trait,这表示它不会使用它所在的 Region 之前的任何值。与之相对的是 `for`,就不是 `IsolatedFromAbove`,因为它要使用上下文的值。
1199 |
1200 | 这里使用 `AnyRegion`,MLIR 还为我们提供了一些其他的 Region 选项,如 `SizedRegion<1>` 表示只有一个基本块的 `Regin`。
1201 |
1202 | 为了能正确地打印函数,我们需要调用 MLIR 提供给我们的 parser 和 printer,这里选 `hasCustomAssemblyFormat=true`。
1203 |
1204 | 随后,要实现好每个 Interface 需要的函数,如 `extraClassDeclaration` 里面的一样。
1205 |
1206 | ```tablegen
1207 | def FuncOp : ToyOp<"func", [
1208 | IsolatedFromAbove,
1209 | FunctionOpInterface,
1210 | /* Symbol, */ /* Symbol 会自动被 FunctionOpInterface 加上 */
1211 | /* CallableOpInterface, */ /* CallOpInterface 会自动被 FunctionOpInterface 加上 */
1212 | RegionKindInterface]> {
1213 | let summary = "function";
1214 | let arguments = (ins
1215 | SymbolNameAttr:$sym_name,
1216 | TypeAttrOf:$function_type,
1217 | // FunctionOpInterface 需要两个 Attr 来记录 arg 和 res 的名字
1218 | OptionalAttr:$arg_attrs,
1219 | OptionalAttr:$res_attrs
1220 | );
1221 | dag regions = (region AnyRegion:$body);
1222 | let hasCustomAssemblyFormat = true;
1223 | let extraClassDeclaration = [{
1224 | // Method of FunctionOpInterface
1225 | mlir::Region * getCallableRegion() {return &getBody();}
1226 | // getFunctionType 函数会自动生成
1227 | // mlir::FunctionType getFunctionType();
1228 |
1229 | // Method of CallableOpInterface
1230 | llvm::ArrayRef getArgumentTypes() {return getFunctionType().getInputs();}
1231 | llvm::ArrayRef getResultTypes() {return getFunctionType().getResults();}
1232 |
1233 | // Method of RegionKindInterface
1234 | static mlir::RegionKind getRegionKind(unsigned idx) { return mlir::RegionKind::SSACFG; }
1235 | }];
1236 | }
1237 | ```
1238 |
1239 | 然后在 `toy.cpp`,使用 MLIR 自带的 function interface 来 parse 和 print。
1240 |
1241 | ```cpp
1242 | #include "mlir/Interfaces/FunctionImplementation.h"
1243 | using namespace mlir;
1244 |
1245 | ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
1246 | auto buildFuncType = [](auto & builder, auto argTypes, auto results, auto, auto) {
1247 | return builder.getFunctionType(argTypes, results);
1248 | };
1249 | return function_interface_impl::parseFunctionOp(
1250 | parser, result, false,
1251 | getFunctionTypeAttrName(result.name), buildFuncType,
1252 | getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name)
1253 | );
1254 | }
1255 |
1256 | void FuncOp::print(OpAsmPrinter &p) {
1257 | function_interface_impl::printFunctionOp(
1258 | p, *this, false, getFunctionTypeAttrName(),
1259 | getArgAttrsAttrName(), getResAttrsAttrName());
1260 | }
1261 | ```
1262 |
1263 | #### 7.8.3. 定义 Call
1264 |
1265 | 使用 CallOpInterface 就行了,需要写一下 Interface 函数。
1266 |
1267 | ```tablegen
1268 | def CallOp : ToyOp<"call", [CallOpInterface]> {
1269 | let summary = "call operation";
1270 | let arguments = (ins SymbolRefAttr:$callee, Variadic:$arg_operands);
1271 | let results = (outs AnyType:$result);
1272 | let assemblyFormat = "$callee `(` $arg_operands `)` attr-dict `:` functional-type($arg_operands, results)";
1273 | let extraClassDeclaration = [{
1274 | mlir::CallInterfaceCallable getCallableForCallee() {
1275 | return getCalleeAttr();
1276 | }
1277 | void setCalleeFromCallable(mlir::CallInterfaceCallable callee) {
1278 | setCalleeAttr(callee.get());
1279 | }
1280 | }];
1281 | }
1282 | ```
1283 |
1284 | ## 8. 添加 Pass
1285 |
1286 | 上一节讲述了 IR 的定义、输入、输出、成员函数等等。但一个编译器只有 IR 还不够,需要有在 IR 上运行的 Pass。我们这一节介绍如何使用方便快捷的 tablegen 来定义 Pass。
1287 |
1288 | ### 8.1. Pass 工程模板
1289 |
1290 | 1. `include/ToyPasses.td`:描述 Pass 文件
1291 |
1292 | ```tablegen
1293 | include "mlir/Pass/PassBase.td"
1294 |
1295 | def ConvertToyToArith : Pass<"convert-toy-to-arith"> {
1296 | let summary = "Convert Toy To Arith";
1297 | let constructor = "toy::createConvertToyToArithPass()";
1298 | }
1299 | ```
1300 |
1301 | 2. `include/CMakeLists.txt`:添加 tablegen
1302 |
1303 | ```cmake
1304 | set(LLVM_TARGET_DEFINITIONS ToyPasses.td)
1305 | mlir_tablegen(ToyPasses.h.inc -gen-pass-decls)
1306 | add_public_tablegen_target(MLIRToyTransformsIncGen)
1307 | ```
1308 |
1309 | 3. `include/ToyPasses.h`:Pass 的头文件
1310 | ```cpp
1311 | namespace toy {
1312 | // 先生成定义
1313 | #define GEN_PASS_DECL
1314 | #include "toy/ToyPasses.h.inc"
1315 |
1316 | // 在写 create 函数表
1317 | std::unique_ptr createConvertToyToArithPass();
1318 |
1319 | // 生成注册函数
1320 | #define GEN_PASS_REGISTRATION
1321 | #include "toy/ToyPasses.h.inc"
1322 | }
1323 | ```
1324 |
1325 | 4. `lib/Transforms/CMakeLists.txt`:添加 library
1326 | ```cmake
1327 | add_mlir_library(
1328 | ToyTransforms
1329 | ConvertToyToArith.cpp
1330 | DEPENDS MLIRToyTransformsIncGen
1331 | )
1332 | ```
1333 |
1334 | 5. `lib/Transforms/ConvertToyToArith.cpp`:一个基本实现
1335 | ```cpp
1336 | #define GEN_PASS_DEF_CONVERTTOYTOARITH
1337 | #include "toy/ToyPasses.h"
1338 | #include "llvm/Support/raw_ostream.h"
1339 |
1340 | struct ConvertToyToArithPass :
1341 | toy::impl::ConvertToyToArithBase
1342 | {
1343 | // 使用父类的构造函数
1344 | using toy::impl::ConvertToyToArithBase::ConvertToyToArithBase;
1345 | void runOnOperation() final {
1346 | getOperation()->print(llvm::errs());
1347 | }
1348 | };
1349 |
1350 | std::unique_ptr toy::createConvertToyToArithPass() {
1351 | return std::make_unique();
1352 | }
1353 | ```
1354 |
1355 | 6. 注册 Pass:`tools/toy-opt/toy-opt.cpp`
1356 |
1357 | ```cpp
1358 | toy::registerPasses();
1359 | return asMainReturnCode(MlirOptMain(argc, argv, "toy-opt", registry));
1360 | ```
1361 |
1362 | 7. 测试:`./ex5-opt -convert-toy-to-arith ../ex5-pass/ex5.mlir`
1363 |
1364 | ### 8.2. Pass 定义详解
1365 |
1366 | #### 8.2.1. 指定 Pass 在哪个 Op 上运行
1367 |
1368 | 参考 [Pass Infrastructure](https://mlir.llvm.org/docs/PassManagement/),MLIR 的 Pass 有下面几种:
1369 |
1370 | * OperationPass:在某个固定类型的 Op 上运行的 Pass
1371 | * InterfacePass:在特定的 OpInterface 上运行的 Pass
1372 |
1373 | 它们两个的区别是,Pass 里面 `getOperation()` 返回的是 Operation 还是 Interface。
1374 |
1375 | 默认情况下,`Pass<"convert-toy-to-arith">` 定义了一个在任意 `Operation*` 上都能运行的 Pass,如果要定义在指定 Operation 上运行的 Pass,可以使用下面的定义。注意,此时使用了`toy::FuncOp`,在 `ToyPasses.h` 文件里需要 include 对应的头文件,防止找不到名字。
1376 |
1377 | ```tablegen
1378 | def ConvertToyToArith : Pass<"convert-toy-to-arith", "toy::FuncOp"> {
1379 | let summary = "Convert Toy To Arith";
1380 | let constructor = "toy::createConvertToyToArithPass()";
1381 | }
1382 | ```
1383 |
1384 | #### 8.2.2. 带参数的 Pass
1385 |
1386 | 首先需要在 tablgen 文件里写上参数的定义:
1387 |
1388 | ```tablegen
1389 | def IsolateTopModule : Pass<"convert-toy-to-arith"> {
1390 | let summary = "Convert Toy To Arith";
1391 | let constructor = "toy::createConvertToyToArithPass()";
1392 | let options = [
1393 | // 代码里的名字 命令行的名字 类型 默认值 帮助
1394 | Option<"name", "name", "std::string", "", "help">
1395 | ];
1396 | }
1397 | ```
1398 |
1399 | 然后,在 `ToyPasses.h` 文件里,要修改 create 函数的定义:
1400 |
1401 | ```cpp
1402 | std::unique_ptr createConvertToyToArithPass(
1403 | ConvertToyToArithOptions options={}
1404 | );
1405 | ```
1406 |
1407 | 在实现 create 函数的时候,也要带上参数:
1408 | ```cpp
1409 | struct ConvertToyToArithPass :
1410 | toy::impl::ConvertToyToArithBase
1411 | {
1412 | // 使用父类的构造函数
1413 | using toy::impl::ConvertToyToArithBase::ConvertToyToArithBase;
1414 | void runOnOperation() final {
1415 | llvm::errs() << "get name: " << name << "\n";
1416 | }
1417 | };
1418 |
1419 | std::unique_ptr toy::createConvertToyToArithPass(
1420 | ConvertToyToArithOptions options) {
1421 | return std::make_unique(options);
1422 | }
1423 | ```
1424 |
1425 | 配置参数的方法:`ex5-opt -convert-toy-to-arith="name=xxx" ../ex5-pass/ex5.mlir`
1426 |
1427 | ### 8.3. 简单的 DCE Pass 实现
1428 |
1429 | Pass 的实现,就是灵活使用 IR 的遍历与修改。我们弄一个简单 DCE Pass 作为 Example
1430 |
1431 | #### 8.3.1. 定义
1432 |
1433 | ```tablegen
1434 | def DCE : Pass<"toy-dce", "toy::FuncOp"> {
1435 | let summary = "dce";
1436 | let constructor = "toy::createDCEPass()";
1437 | }
1438 | ```
1439 |
1440 | #### 8.3.2. 实现
1441 |
1442 | ```cpp
1443 | struct DCEPass : toy::impl::DCEBase {
1444 | void visitAll(llvm::DenseSet &visited, Operation * op) {
1445 | if(visited.contains(op)) return;
1446 | visited.insert(op);
1447 | for(auto operand: op->getOperands())
1448 | if(auto def = operand.getDefiningOp())
1449 | visitAll(visited, def);
1450 | }
1451 | void runOnOperation() final {
1452 | llvm::DenseSet visited;
1453 | // 遍历所有 Return,把 Return 可达的加入 visited 集合
1454 | getOperation()->walk([&](toy::ReturnOp op) {
1455 | visitAll(visited, op);
1456 | });
1457 | llvm::SmallVector opToRemove;
1458 | // 将不可达的加入 opToRemove 集合
1459 | getOperation().walk([&](Operation * op) {
1460 | if(op == getOperation()) return;
1461 | if(!visited.contains(op)) opToRemove.push_back(op);
1462 | });
1463 | // 反向 erase
1464 | for(auto v: reverse(opToRemove)) {
1465 | v->erase();
1466 | }
1467 | }
1468 | };
1469 | ```
1470 |
1471 | ## 9. Pattern Rewrite
1472 |
1473 | pattern rewrite 是 MLIR 的一大特色。Pattern 会匹配 IR 的一个子图,然后将其更改为新的格式。MLIR 会为我们自动调度 pattern,让 IR 的变换更加简单。
1474 |
1475 | 很多 IR 的操作都可以看作是 Pattern Rewrite:
1476 | * 算术优化,如 x*2 优化为 x+x ,可以看作是对表达式做模式替换
1477 | * 表达式 Lowering,可以看作是把 HighLevel Op 替换为 LowLevel Op
1478 |
1479 | 在这一节,我们使用 Pattern Rewrite 来把 toy 里的 Op 转换为 Arith 里的 Op。
1480 |
1481 | ### 9.1. Pattern Rewrite
1482 |
1483 | #### 9.1.1. 描述 Pattern
1484 |
1485 | `matchAndRewrite` 返回 success 表示能够 match,返回 failure 表示不能 match。如果能 match,就通过 rewriter 改写。rewriter 实现了一套完整的改写 API。
1486 |
1487 | ```c++
1488 | struct AddOpPat: OpRewritePattern {
1489 | using OpRewritePattern::OpRewritePattern;
1490 | LogicalResult matchAndRewrite(AddOp op, PatternRewriter & rewriter) const {
1491 | auto inputs = to_vector(op.getInputs());
1492 | auto result = inputs[0];
1493 | for(size_t i = 1; i< inputs.size(); i++) {
1494 | result = rewriter.create(op->getLoc(), result, inputs[i]);
1495 | }
1496 | rewriter.replaceOp(op, ValueRange(result));
1497 | return success();
1498 | }
1499 | };
1500 | ```
1501 |
1502 | #### 9.1.2. 调用 Pattern
1503 |
1504 | 在使用 conversion 的时候,首先要定义 `ConversionTarget`,然后要配置好 `PatternSet`,最后调用 `applyXXX` 驱动函数:
1505 |
1506 | ```c++
1507 | ConversionTarget target(getContext());
1508 | target.addLegalDialect();
1509 | RewritePatternSet patterns(&getContext());
1510 | patterns.add(&getContext());
1511 | if(failed(applyPartialConversion(getOperation(), target, std::move(patterns))))
1512 | signalPassFailure();
1513 | ```
1514 |
1515 | 这里我们使用了 `partialConversion`,MLIR 支持三种 Conversion 模式:
1516 | * `partialConversion`:**如果 Pattern 转换结果是 Legal,则保留转换结果**。如果输入存在 IllegalOp 或 IllegalDialect,立刻报错。
1517 | * `fullConversion`:开始时可能是Illegal的。**调用 Pattern 将其转换,直到全部 Legal 为止**。
1518 | * `greedyPatternRewrite`:**不需要提供 Target,贪心地尝试尽量多次修改**。
1519 |
1520 | 前两个常用于 Dialect Lowering 之中。而`geedyPatternRewrie` 很适合用来写优化,比如我可以写一个把形如 `toy.sub %a, %a` 替换为 `const 0: i32` 的 pattern,希望 MLIR 尽量多优化它。
1521 |
1522 | #### 9.1.3. Depedent Dialect & Linking
1523 |
1524 | 注意,我们将 toy dialect 转换为了 arith dialect,这说明我们的 pass 依赖 arith ,要添加依赖:
1525 |
1526 | ```c++
1527 | void getDependentDialects(DialectRegistry ®istry) const final {
1528 | registry.insert();
1529 | }
1530 | ```
1531 |
1532 | 同时,在 `opt` 程序里还要注册 arith,
1533 |
1534 | ```c++
1535 | registry.insert();
1536 | ```
1537 |
1538 | 以及链接上 arith,这是我们的 Transform 依赖 arith,所以 arith 应该加载 transform 的链接列表中。
1539 |
1540 | ```cmake
1541 | add_mlir_library(
1542 | ToyTransforms
1543 | ConvertToyToArith.cpp
1544 | DCE.cpp
1545 | DEPENDS MLIRToyTransformsIncGen
1546 | LINK_LIBS MLIRArithDialect # here
1547 | )
1548 | ```
1549 |
1550 | 使用下面的命令来验证结果:
1551 |
1552 | ```
1553 | ./ex6-opt --convert-toy-to-arith --toy-dce ../ex6-pattern/ex6.mlir
1554 | ```
1555 |
1556 |
1557 | ##### debug 的方法
1558 |
1559 | 可以用 `--debug` 来启动程序,程序会打印出转换的详细过程。
1560 |
1561 | ```
1562 | ./ex6-opt --debug --convert-toy-to-arith ../ex6-pattern/ex6.mlir
1563 | ```
1564 |
1565 | ### 9.2. Dialect Convertion (Type Conversion)
1566 |
1567 | Dialect 除了 Op 之外,还有 Type。在进行 Dialect 之间的转换的时候,对 Type 的改写也很重要。
1568 |
1569 | MLIR 对 Type 做改写的方法是用 `TypeConverter` 完成的, `TypeConverter` 有三个功能:
1570 |
1571 | 1. `addConversion`:添加一个 Type 的转换规则
1572 | 2. `addTargetMaterialization`:生成将 SourceType 转换为 TargetType 的代码块
1573 | 3. `addSourceMaterialization`:生成将 TargetType 转换回 SourceType 的代码块
1574 |
1575 | 这三个里面最重要的是 1,剩下两个一般不需要自己实现。
1576 |
1577 | 为了做示范,我们定义一个自己的 `toy.int` 类型,它可以被转换为 `Integer` 类型。这里略过类型定义的部分,详细请看 [自定义类型](#10-自定义-type)。
1578 |
1579 | #### 9.2.1. TypeConverter
1580 |
1581 | 首先,我们要声明一个 DialectConverter,然后我们要为其添加类型转换规则。下面的代码添加了 ToyIntegerType 到 IntegerType 的转换。MLIR 会使用神奇的模板元编程的方法,获取传入函数的参数和返回值类型,来判断是什么类型到什么类型的转换。
1582 |
1583 | ```c++
1584 | TypeConverter converter;
1585 | converter.addConversion([&](ToyIntegerType t) -> std::optional {
1586 | return IntegerType::get(&getContext(), t.getWidth());
1587 | });
1588 | ```
1589 |
1590 | #### 9.2.2. Conversion Pattern:自动做 Operand 的类型转换
1591 |
1592 | 我们用 ConversionPattern 来自动做类型转换。ConversionPattern 与 RewritePattern 不同的是,它多了一个 `Adaptor`。`Adaptor` 在前面 [InferTypeOpInterface](#772-类型推断infertypeopinterface) 介绍到,`Adaptor` 是只有 operands 没有 results 的中间态。
1593 |
1594 | MLIR 在调用 ConversionPattern 之前,会先尝试将 op 的 Operand 全部转换为目标格式,如果不能转换就保留原来的。并且将转换之后的 operand 储存在 Adaptor 里面。
1595 |
1596 | 在 replace 的时候,类型也可以和原来的不一样(但必须能转换过去),MLIR 会自动处理类型转换的问题。
1597 |
1598 | ```c++
1599 | struct AddOpPat: OpConversionPattern {
1600 | using OpConversionPattern::OpConversionPattern;
1601 | LogicalResult matchAndRewrite(AddOp op, AddOpAdaptor adaptor, ConversionPatternRewriter & rewriter) const {
1602 | auto inputs = to_vector(adaptor.getInputs());
1603 | auto result = inputs[0];
1604 | for(size_t i = 1; i< inputs.size(); i++) {
1605 | assert(inputs[i]);
1606 | result = rewriter.create(op->getLoc(), result, inputs[i]);
1607 | }
1608 | rewriter.replaceOp(op, ValueRange(result));
1609 | return success();
1610 | }
1611 | };
1612 | ```
1613 |
1614 | ##### 使用 MLIR 自带的 FuncOpConversion
1615 |
1616 | 在对函数做类型转换的时候,既需要对 Region 的参数表做转换,还需要对函数类型做转换。MLIR为我们提供了默认的 Pattern:
1617 |
1618 | ```c++
1619 | populateFunctionOpInterfaceTypeConversionPattern(patterns, converter);
1620 | ```
1621 |
1622 | #### 9.2.3. 类型转换的细节与 Debug
1623 |
1624 | 在使用类型转换时候,可以用 `--debug` 来启动程序,程序会打印出转换的详细过程。
1625 |
1626 | 一般情况下,mlir 做类型转换是通过一个特殊的 Op 来实现的: `builtin.unrealized_conversion_cast`
1627 |
1628 | 例如,mlir 要对下面的代码做类型转换:
1629 |
1630 | ```mlir
1631 | %a = toy.constant 0: !toy.int<32>
1632 | %b = toy.add %a, %a: !toy.int<32>
1633 | ```
1634 |
1635 | 当然,mlir 一般都是从前往后做替换的,这样更不容易插入 unrealized。可以在 apply 的时候配置 config 改变顺序,为了展示,我们假设 mlir 从%b开始匹配。
1636 | `toy.add`,它的输入是 `%a`,类型为 `!toy.int<32>`,转换为 `i32`,需要做 TargetMaterialization,由于用户没有注册 materialization,就插入一个 unrealized 的转换:
1637 |
1638 | ```mlir
1639 | %a = toy.constant 0: !toy.int<32>
1640 | %a_1 = builtin.unrealized_conversion_cast %a : !toy.int<32> to i32
1641 | %b = arith.add %a_1, %a_1 : i32
1642 | ```
1643 |
1644 | 接下来,再匹配 `toy.constant`,被替换为了 `arith.constant`,mlir 发现输出类型发生了变化,做 SourceMaterialization,同样插入 unrealized
1645 |
1646 | ```mlir
1647 | %a_2 = arith.constant 0: i32
1648 | %a = builtin.unrealized_conversion_cast %a_2 : i32 to !toy.int<32>
1649 | %a_1 = builtin.unrealized_conversion_cast %a : !toy.int<32> to i32
1650 | %b = arith.add %a_1, %a_1 : i32
1651 | ```
1652 |
1653 | 最后,mlir 会尝试把所有的 unrealized 转换移除。上面 `i32` 被转换成了 `!toy.int<32>`,又被转换了回去,是无效转换,需要移除,最后变成:
1654 |
1655 | ```mlir
1656 | %a = arith.constant 0: i32
1657 | %b = arith.add %a, %a : i32
1658 | ```
1659 |
1660 | #### 9.2.4. 使用自己的 materialization
1661 |
1662 | 如果用户注册了自己的 materialization 方法,MLIR 就会使用用户注册的 materilzation。
1663 |
1664 | 一个使用自己 materialization 的场景:我们自己定义了 `float32` 复数类型,转换的时候要把它转换成 `float8`。我们肯定希望尽量少地调用函数做类型转换 `float8`。但如果`float32`是函数参数,别人要调用这个函数不能随便改,就只能强行转换了。
1665 |
1666 | 例如,我们可以直接把 `unrealized_conversion_cast` 注册为默认 materialization,这样在 debug 的时候很方便。
1667 |
1668 | ```c++
1669 | converter.addTargetMaterialization([](OpBuilder& builder, Type /* 对所有 SourceType 注册 */ resultType, ValueRange inputs, Location loc) -> std::optional {
1670 | return builder.create(loc, resultType, inputs).getResult(0);
1671 | });
1672 | ```
1673 |
1674 | 把关于函数的 target 注释掉,可以观察程序的结果:
1675 |
1676 | ```c++
1677 | // target.addDynamicallyLegalOp([](FuncOp f) {
1678 | // return llvm::all_of(f.getArgumentTypes(),
1679 | // [](Type t) {return !isa(t);});
1680 | // });
1681 | ```
1682 |
1683 | ```c++
1684 | ./ex7-opt --convert-toy-to-arith ../ex7-convert/ex7.mlir
1685 | ```
1686 |
1687 | ```mlir
1688 | toy.func @add(%arg0: !toy.int<32>, %arg1: !toy.int<32>) -> !toy.int<32> {
1689 | %0 = builtin.unrealized_conversion_cast %arg0 : !toy.int<32> to i32
1690 | %1 = builtin.unrealized_conversion_cast %arg1 : !toy.int<32> to i32
1691 | %2 = arith.addi %0, %1 : i32
1692 | toy.ret %2 : i32
1693 | }
1694 | ```
1695 |
1696 | ### 9.3. 使用 MLIR 里已有的 Pattern 做多步转换
1697 |
1698 | MLIR 为我们提供了模块化的 PatternRewrite API。几乎所有的 Conversion 都有对应的 populateXXXPatterns 函数。
1699 |
1700 | 例如,我们想要一次性将 `toy` 转换到 `llvm`,可以先自己写 pattern 把 `toy` 转换到 `arith`,再添加 `arith` 的 pattern 将其转换为 `LLVM`:
1701 |
1702 | ```c++
1703 | #include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
1704 | #include "mlir/Conversion/LLVMCommon/TypeConverter.h"
1705 | #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
1706 |
1707 | ConversionTarget target(getContext());
1708 | target.addLegalDialect();
1709 | LLVMTypeConverter converter(&getContext());
1710 | RewritePatternSet patterns(&getContext());
1711 | patterns.add(&getContext());
1712 | arith::populateArithToLLVMConversionPatterns(converter, patterns); // 使用已有 pattern
1713 | if(failed(applyPartialConversion(getOperation(), target, std::move(patterns))))
1714 | signalPassFailure();
1715 | ```
1716 |
1717 | 其他头文件,需要链接的库文件,请看 `ex6` 里的代码。
1718 |
1719 | ## 10. 自定义 Type
1720 |
1721 | 参考 `ex7`,自定义类型的方法:
1722 |
1723 | ```tablegen
1724 | // ToyDialect.td
1725 | def ToyDialect : Dialect {
1726 | let name = "toy";
1727 | let cppNamespace = "::toy";
1728 | let summary = "Toy Dialect";
1729 | let useDefaultTypePrinterParser = true; // 新增
1730 | let extraClassDeclaration = [{
1731 | void registerTypes();
1732 | }];
1733 | }
1734 |
1735 |
1736 | // ToyTypes.td
1737 | class ToyType traits=[]>: TypeDef;
1738 | def ToyInteger: ToyType<"ToyInteger"> {
1739 | let mnemonic = "int";
1740 | let parameters = (ins "uint64_t":$width);
1741 | let assemblyFormat = "`<` $width `>`";
1742 | }
1743 | ```
1744 |
1745 | ```c++
1746 | #include "toy/ToyTypes.h"
1747 | #include "mlir/IR/DialectImplementation.h"
1748 | #include "llvm/ADT/StringExtras.h"
1749 | #include "llvm/ADT/StringSwitch.h"
1750 | #include "llvm/ADT/TypeSwitch.h"
1751 | #define GET_TYPEDEF_CLASSES
1752 | #include "toy/ToyTypes.cpp.inc"
1753 |
1754 | void ToyDialect::initialize() {
1755 | addOperations<
1756 | #define GET_OP_LIST
1757 | #include "toy/Toy.cpp.inc"
1758 | >();
1759 | registerTypes(); // 增加
1760 | }
1761 |
1762 | void ToyDialect::registerTypes() {
1763 | addTypes<
1764 | #define GET_TYPEDEF_LIST
1765 | #include "toy/ToyTypes.cpp.inc"
1766 | >();
1767 | }
1768 | ```
1769 |
1770 | ## 11. TIPS
1771 |
1772 | ### 11.1. 如何找头文件、找想要的函数
1773 |
1774 | 首先,对于常用的头文件,可以都过目一下函数列表,包括:
1775 |
1776 | * `llvm/ADT/*` 里面的数据结构
1777 | * `mlir/IR/CommonAttrConstraints.td`
1778 | * `mlir/IR/CommonTypeConstraints.td`
1779 |
1780 | MLIR 的 Dialect 文件结构都比较整齐,`mlir/Dialect/XXX/IR/XXX.h`
1781 |
1782 | 其他的函数/头文件,建议开个 vscode 到 mlir 源码目录,使用全局搜索来找。
1783 |
1784 | ### 11.2. 如何找需要链接的库
1785 |
1786 | 首先,找到你 include 的头文件,如 `mlir/Dialect/Func/IR/FuncOps.h`。
1787 |
1788 | 然后,找到这个头文件对应的 cpp 文件,`lib/Dialect/Func/IR/FuncOps.cpp`。
1789 |
1790 | 从 cpp 文件逐步往上找 `CMakeLists.txt`,检查里面的 `add_mlir_dialect_library` 里的库文件名。
1791 |
1792 | ### 11.3. 如何加快编译速度
1793 |
1794 | MLIR 经常会链接出上百 M 甚至上 G 的文件,不同的链接器对性能有很大影响,使用 `lld` (llvm 链接器) 似乎会比 `ld` 快非常多,下面的命令可以让 CMAKE 强制使用 lld(你需要先安装 llvm 编译工具包)。
1795 |
1796 | ```bash
1797 | cmake .. -DCMAKE_CXX_FLAGS="-fuse-ld=lld"
1798 | ```
1799 |
1800 | ### 11.4. 去 MLIR 里抄代码
1801 |
1802 | MLIR 为我们写好了大量的 Dialect,我们想要的功能,那些 dialect 多半都已经实现过了。
1803 |
1804 | 可以用 `mlir-opt --help`,`mlir-opt --help-hidden` 看看有那些 dialect 哪些选项,找到可能是和自己想要做的相似的,然后过去看代码,边看边抄大概就能实现好了。
1805 |
1806 | ## 12. MLIR 的批判:C++ v.s. Rust
1807 |
1808 | > 这一段都是我的个人想法,可能会比较偏激。
1809 |
1810 | MLIR 只能用 g++ 编译,用 clang++ 编译会 Runtime Error。这充分地说明了一个事实:MLIR 这座庞大的大厦,是建立在脆弱的 ub (undefined behavior) 之上的。
1811 |
1812 | MLIR Context,在 rust 眼里,实际上就是一个 Interner,把类型转换为指针,来去重和加速类型比较。但而为了实现它,MLIR 使用了晦涩难懂又麻烦的 Pointer, Storage 的模式。
1813 |
1814 | Tablegen 可以快速地描述一段 IR,前提是去掉 debug 需要的时间。
1815 |
1816 | 多返回值 Op 虽然增加了 IR 的表达力,但让数据流分析变得复杂。实际上,多返回值 Op 几乎不存在。但我依然不得不为罕见的部分 Op 做特判,处理多返回值的情况。
1817 |
1818 | 另外,作为 C++ 的通病,MLIR 指针混沌得让人绝望。而更让人绝望的是,MLIR 要求任意操作过程中,IR 总是合法的。我们不能插入空指针,也不能随意删除一个变量。可以说,当你走出了 PatternRewrite 的舒适区域,想要做一些复杂的 Inline, Group, Partition 操作时,Segmentation Fault 总是与你形影不离。
1819 |
1820 | mlir 创新地把 Op 同构地看作 operand, attribute, result 的集合,具体的 Op 只是这个集合的解释方法。但其本质,就是定制的序列化和反序列化系统。而困惑的是,这样一个输入输出系统,在运行过程中一直存在,我们随时都在反序列化 Op 来获取 operand,更新 operand 之后又序列化到通用表示上。为了完成这样的序列化、反序列化,mlir 创造了令人惊叹的冗余代码,巨大的二进制文件,令人困惑的函数定义,无处不在的 Segmentation Fault 陷阱,和未知的性能提升。
1821 |
1822 | 我觉得,一个更好的 IR System 应该是:
1823 |
1824 | * 严格 SSA 的:所有操作都有返回值,多返回值看作 Tuple,无返回值看作空 Tuple
1825 | * 异构Op:Op 不存在 operand, attr, result 的统一形式,而是异构的,序列化按需进行。
1826 | * 无状态的:不需要 Interner。Interner 是为了处理大量的复制。用 Rc 来处理复制,实现专门的 Pass 来去重。
1827 | * 控制流、数据流分离的:控制流和数据流用不同的结构来储存,可以做分离的分析,而不是存在一个指针表里面
1828 |
1829 | ## 13. Issue & Reply
1830 |
1831 | 本文档仅作教学,本人不负责解决使用 mlir 中遇到的任何问题。作为一个要使用 mlir 的人,应该做好遭遇玄学 bug 的觉悟。
1832 |
--------------------------------------------------------------------------------
/ex1-io/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_executable(toy-ex1-io ex1-io.cpp)
2 | target_link_libraries(
3 | toy-ex1-io
4 | MLIRIR
5 | MLIRParser
6 | MLIRFuncDialect
7 | MLIRArithDialect
8 | )
9 |
--------------------------------------------------------------------------------
/ex1-io/ex1-io.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/IR/AsmState.h"
2 | #include "mlir/IR/BuiltinOps.h"
3 | #include "mlir/IR/MLIRContext.h"
4 | #include "mlir/Parser/Parser.h"
5 | #include "mlir/Support/FileUtilities.h"
6 |
7 | #include "mlir/Dialect/Func/IR/FuncOps.h"
8 | #include "mlir/Dialect/Arith/IR/Arith.h"
9 | #include "llvm/Support/raw_ostream.h"
10 |
11 | using namespace mlir;
12 |
13 | int main(int argc, char ** argv) {
14 | MLIRContext ctx;
15 | // 首先,注册需要的 dialect
16 | ctx.loadDialect();
17 | // 读入dialect
18 | auto src = parseSourceFile(argv[1], &ctx);
19 | // 输出dialect
20 | src->print(llvm::outs());
21 | // 简单的输出,在 debug 的时候常用
22 | src->dump();
23 | return 0;
24 | }
--------------------------------------------------------------------------------
/ex1-io/ex1.mlir:
--------------------------------------------------------------------------------
1 | func.func @test(%a: i32, %b: i32) -> i32 {
2 | %c = arith.addi %a, %b : i32
3 | func.return %c : i32
4 | }
--------------------------------------------------------------------------------
/ex2-build-new/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.13.4)
2 |
3 | project(toy-build LANGUAGES CXX C)
4 |
5 | set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to conform to")
6 |
7 | message(STATUS "Using MLIRConfig.cmake in: ${MLIR_DIR}")
8 |
9 | find_package(MLIR REQUIRED CONFIG)
10 | list(APPEND CMAKE_MODULE_PATH "${MLIR_CMAKE_DIR}")
11 | list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
12 |
13 | include(TableGen)
14 | include(AddLLVM)
15 | include(AddMLIR)
16 | include(HandleLLVMOptions)
17 |
18 | include_directories(${MLIR_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS})
19 |
20 | add_executable(toy-build toy-build.cpp)
21 | target_link_libraries(
22 | toy-build
23 | MLIRMemRefDialect
24 | MLIRFuncDialect
25 | MLIRAffineDialect
26 | MLIRArithDialect
27 | MLIRAffineUtils
28 | )
--------------------------------------------------------------------------------
/ex2-build-new/cgeist-input.c:
--------------------------------------------------------------------------------
1 | // cgeist run cmd
2 | // cgeist ArraySum.c --function=* -S --memref-fullrank -o test.mlir
3 |
4 | #define N 10
5 |
6 | float ArraySum(float a[N]) {
7 | // Polygeist的pragma,会优先生成affine
8 | #pragma scop
9 | float sum = 0;
10 | for (int i = 0; i < N; i++) {
11 | sum += a[i];
12 | }
13 | return sum;
14 | // Polygeist的pragma,会优先生成affine
15 | #pragma endscop
16 | }
17 |
--------------------------------------------------------------------------------
/ex2-build-new/cgeist-output.mlir:
--------------------------------------------------------------------------------
1 | module attributes {} {
2 | func.func @ArraySum(%arg0: memref<10xf32>) -> f32 attributes {llvm.linkage = #llvm.linkage} {
3 | %cst = arith.constant 0.000000e+00 : f32
4 | %alloca = memref.alloca() : memref
5 | affine.store %cst, %alloca[] : memref
6 | affine.for %arg1 = 0 to 10 {
7 | %1 = affine.load %arg0[%arg1] : memref<10xf32>
8 | %2 = affine.load %alloca[] : memref
9 | %3 = arith.addf %2, %1 : f32
10 | affine.store %3, %alloca[] : memref
11 | }
12 | %0 = affine.load %alloca[] : memref
13 | return %0 : f32
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ex2-build-new/cmake-cmd.sh:
--------------------------------------------------------------------------------
1 | mkdir build; cd build
2 | # 请根据实际情况修改
3 | export export LLVM_BUILD_DIR=~/llvm-project/build
4 | cmake -G Ninja .. \
5 | -DMLIR_DIR=$LLVM_BUILD_DIR/lib/cmake/mlir \
6 | -DLLVM_ENABLE_ASSERTIONS=ON \
7 | -DCMAKE_BUILD_TYPE=Debug
8 | ninja
9 |
--------------------------------------------------------------------------------
/ex2-build-new/toy-build.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/IR/AsmState.h"
2 | #include "mlir/IR/Builders.h"
3 | #include "mlir/IR/BuiltinAttributes.h"
4 | #include "mlir/IR/BuiltinOps.h"
5 | #include "mlir/IR/BuiltinTypes.h"
6 | #include "mlir/IR/MLIRContext.h"
7 | #include "mlir/Support/FileUtilities.h"
8 |
9 | #include "mlir/Dialect/Affine/IR/AffineOps.h"
10 | #include "mlir/Dialect/Affine/IR/AffineValueMap.h"
11 | #include "mlir/Dialect/Arith/IR/Arith.h"
12 | #include "mlir/Dialect/Func/IR/FuncOps.h"
13 | #include "mlir/Dialect/MemRef/IR/MemRef.h"
14 |
15 | int main(int argc, char **argv) {
16 | mlir::MLIRContext ctx;
17 |
18 | // Context 加载FuncDialect,MemRefDialect, AffineDialect 和 ArithDialect
19 | ctx.loadDialect();
21 |
22 | // 创建 OpBuilder
23 | mlir::OpBuilder builder(&ctx);
24 |
25 | // 创建IR的根,ModuleOp
26 | auto module = builder.create(builder.getUnknownLoc());
27 |
28 | // 设置插入点
29 | builder.setInsertionPointToEnd(module.getBody());
30 |
31 | // 创建 函数
32 | auto f32 = builder.getF32Type();
33 | // 创建 长度为 10 的数组
34 | auto memref = mlir::MemRefType::get({10}, f32);
35 | // 创建 func,函数名为ArraySum,输入是刚创建的数组,输出是f32
36 | auto func = builder.create(
37 | builder.getUnknownLoc(), "ArraySum",
38 | builder.getFunctionType({memref}, {f32}));
39 |
40 | // 设置插入点,插入到func所建的block后面
41 | builder.setInsertionPointToEnd(func.addEntryBlock());
42 | // 创建浮点类型的1.0
43 | mlir::Value constantFloatZeroVal = builder.create(
44 | builder.getUnknownLoc(), builder.getF32FloatAttr(0.0));
45 | // 存储sum的memref
46 | auto sumMemref = mlir::MemRefType::get({}, f32);
47 | // 创建sum的AllocaOp
48 | mlir::Value sumMemrefVal = builder.create(
49 | builder.getUnknownLoc(), sumMemref);
50 | // 创建访问sum的空AffineMap
51 | auto sumMap = builder.getEmptyAffineMap();
52 | // 使用 store 初始化为0
53 | builder.create(
54 | builder.getUnknownLoc(), constantFloatZeroVal, sumMemrefVal, sumMap,
55 | mlir::ValueRange());
56 |
57 | // 创建 lower bound AffineMap
58 | auto lbMap = mlir::AffineMap::get(/*dimCount=*/0, /*symbolCount=*/0,
59 | builder.getAffineConstantExpr(0),
60 | builder.getContext());
61 | // 创建 upper bound AffineMap
62 | auto ubMap = mlir::AffineMap::get(/*dimCount=*/0, /*symbolCount=*/0,
63 | builder.getAffineConstantExpr(10),
64 | builder.getContext());
65 | // 创建循环
66 | auto affineForOp = builder.create(
67 | builder.getUnknownLoc(), mlir::ValueRange(), lbMap, mlir::ValueRange(),
68 | ubMap, 1);
69 | auto savedIP = builder.saveInsertionPoint();
70 | builder.setInsertionPointToStart(affineForOp.getBody());
71 |
72 | // 为 %arg0[%arg1] 创建 AffineMap,表达式为 (d0) -> (d0)。即输入d0,结果为d0
73 | auto forLoadMap = mlir::AffineMap::get(
74 | /*dimCount=*/1, /*symbolCount=*/0, builder.getAffineDimExpr(0),
75 | builder.getContext());
76 | // Load %arg0[%arg1]
77 | mlir::Value affineLoad = builder.create(
78 | builder.getUnknownLoc(), func.getArgument(0), forLoadMap,
79 | mlir::ValueRange(affineForOp.getBody()->getArgument(0)));
80 | // Load %alloca[]
81 | mlir::Value sumInforLoad = builder.create(
82 | builder.getUnknownLoc(), sumMemrefVal, sumMap, mlir::ValueRange());
83 | // %alloca[] + %arg0[%arg1]
84 | mlir::Value add = builder.create(
85 | builder.getUnknownLoc(), sumInforLoad, affineLoad);
86 | // 保存到 %alloca[]
87 | builder.create(
88 | builder.getUnknownLoc(), add, sumMemrefVal, sumMap, mlir::ValueRange());
89 |
90 | // 恢复InsertionPoint
91 | builder.restoreInsertionPoint(savedIP);
92 | // Load %alloca[]
93 | mlir::Value sumLoadVal = builder.create(
94 | builder.getUnknownLoc(), sumMemrefVal, sumMap, mlir::ValueRange());
95 | // 创建以%alloca[]的结果的返回Op
96 | builder.create(builder.getUnknownLoc(), sumLoadVal);
97 | module->print(llvm::outs());
98 | return 0;
99 | }
--------------------------------------------------------------------------------
/ex2-build/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_executable(toy-ex2-build ex2-build.cpp)
2 | target_link_libraries(
3 | toy-ex2-build
4 | MLIRIR
5 | MLIRParser
6 | MLIRFuncDialect
7 | MLIRArithDialect
8 | )
9 |
--------------------------------------------------------------------------------
/ex2-build/ex2-build.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/IR/AsmState.h"
2 | #include "mlir/IR/Builders.h"
3 | #include "mlir/IR/BuiltinAttributes.h"
4 | #include "mlir/IR/BuiltinOps.h"
5 | #include "mlir/IR/BuiltinTypes.h"
6 | #include "mlir/IR/MLIRContext.h"
7 | #include "mlir/IR/Visitors.h"
8 | #include "mlir/Parser/Parser.h"
9 | #include "mlir/Support/FileUtilities.h"
10 |
11 | #include "mlir/Dialect/Func/IR/FuncOps.h"
12 | #include "mlir/Dialect/Arith/IR/Arith.h"
13 | #include "llvm/Support/Casting.h"
14 | #include "llvm/Support/raw_ostream.h"
15 |
16 | #include "llvm/ADT/DenseMap.h"
17 |
18 | using namespace mlir;
19 | using namespace llvm;
20 |
21 | int main(int argc, char ** argv) {
22 | MLIRContext ctx;
23 |
24 | ctx.loadDialect();
25 |
26 | // 创建 OpBuilder
27 | OpBuilder builder(&ctx);
28 | auto mod = builder.create(builder.getUnknownLoc());
29 |
30 | // 设置插入点
31 | builder.setInsertionPointToEnd(mod.getBody());
32 |
33 | // 创建 func
34 | auto i32 = builder.getI32Type();
35 | auto funcType = builder.getFunctionType({i32, i32}, {i32});
36 | auto func = builder.create(builder.getUnknownLoc(), "test", funcType);
37 |
38 | // 添加基本块
39 | auto entry = func.addEntryBlock();
40 | auto args = entry->getArguments();
41 |
42 | // 设置插入点
43 | builder.setInsertionPointToEnd(entry);
44 |
45 | // 创建 arith.addi
46 | auto addi = builder.create(builder.getUnknownLoc(), args[0], args[1]);
47 |
48 | // 创建 func.return
49 | builder.create(builder.getUnknownLoc(), ValueRange({addi}));
50 | mod->print(llvm::outs());
51 | return 0;
52 | }
--------------------------------------------------------------------------------
/ex3-dialect/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR})
2 |
3 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
4 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
5 | add_subdirectory(include/toy)
6 | add_subdirectory(lib)
7 | add_subdirectory(tools/toy-opt)
--------------------------------------------------------------------------------
/ex3-dialect/ex3-cse.mlir:
--------------------------------------------------------------------------------
1 | func.func @test(%a: i32, %b: i32) -> i32 {
2 | %c = "toy.add"(%a, %b): (i32, i32) -> i32
3 | %d = "toy.add"(%a, %b): (i32, i32) -> i32
4 | %e = "toy.add"(%c, %d): (i32, i32) -> i32
5 | %f = "toy.add"(%e, %e): (i32, i32) -> i32
6 | func.return %e : i32
7 | }
--------------------------------------------------------------------------------
/ex3-dialect/ex3.mlir:
--------------------------------------------------------------------------------
1 | func.func @test(%a: i32, %b: i32) -> i32 {
2 | %c = "toy.add"(%a, %b): (i32, i32) -> i32
3 | func.return %c : i32
4 | }
--------------------------------------------------------------------------------
/ex3-dialect/include/toy/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_dialect(Toy toy)
--------------------------------------------------------------------------------
/ex3-dialect/include/toy/Toy.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_TD
2 | #define TOY_TD
3 |
4 | include "toy/ToyDialect.td"
5 | include "toy/ToyOps.td"
6 |
7 | #endif
8 |
--------------------------------------------------------------------------------
/ex3-dialect/include/toy/ToyDialect.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/IR/BuiltinDialect.h"
4 | #include "toy/ToyDialect.h.inc"
5 |
--------------------------------------------------------------------------------
/ex3-dialect/include/toy/ToyDialect.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_DIALECT_TD
2 | #define TOY_DIALECT_TD
3 |
4 | include "mlir/IR/OpBase.td"
5 |
6 | def ToyDialect : Dialect {
7 | let name = "toy";
8 | let cppNamespace = "::toy";
9 | let summary = "Toy Dialect";
10 | }
11 |
12 | #endif
13 |
--------------------------------------------------------------------------------
/ex3-dialect/include/toy/ToyOps.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/IR/BuiltinOps.h"
4 | #include "mlir/IR/Builders.h"
5 |
6 | #include "toy/ToyDialect.h"
7 | #include "mlir/Interfaces/SideEffectInterfaces.h"
8 |
9 | #define GET_OP_CLASSES
10 | #include "toy/Toy.h.inc"
--------------------------------------------------------------------------------
/ex3-dialect/include/toy/ToyOps.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_OPS_TD
2 | #define TOY_OPS_TD
3 |
4 | include "mlir/IR/OpBase.td"
5 | include "toy/ToyDialect.td"
6 | include "mlir/Interfaces/SideEffectInterfaces.td"
7 |
8 | // mnemonic 指名字
9 | class ToyOp traits = []> :
10 | Op;
11 |
12 | // Pure 是 Trait,表示没有 SideEffect 的纯函数
13 | def AddOp : ToyOp<"add", [Pure]> {
14 | let summary = "add operation";
15 | let arguments = (ins Variadic:$inputs);
16 | let results = (outs AnyInteger:$result);
17 | }
18 |
19 | def SubOp : ToyOp<"sub", [Pure]> {
20 | let summary = "sub operation";
21 | let arguments = (ins AnyInteger:$lhs, AnyInteger:$rhs);
22 | let results = (outs AnyInteger:$result);
23 | }
24 |
25 | def ConstantOp : ToyOp<"const", [Pure]> {
26 | let summary = "const operation";
27 | let arguments = (ins APIntAttr:$value);
28 | let results = (outs AnyInteger:$result);
29 | }
30 |
31 | #endif
32 |
--------------------------------------------------------------------------------
/ex3-dialect/lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_library(Toy toy.cpp DEPENDS MLIRToyIncGen)
--------------------------------------------------------------------------------
/ex3-dialect/lib/toy.cpp:
--------------------------------------------------------------------------------
1 | #include "toy/ToyDialect.h"
2 | #include "toy/ToyOps.h"
3 |
4 | #include "toy/ToyDialect.cpp.inc"
5 | #define GET_OP_CLASSES
6 | #include "toy/Toy.cpp.inc"
7 |
8 | using namespace mlir;
9 | using namespace toy;
10 |
11 | void ToyDialect::initialize() {
12 | addOperations<
13 | #define GET_OP_LIST
14 | #include "toy/Toy.cpp.inc"
15 | >();
16 | }
17 |
--------------------------------------------------------------------------------
/ex3-dialect/tools/toy-opt/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_tool(ex3-opt toy-opt.cpp)
2 | target_link_libraries(ex3-opt
3 | PRIVATE Toy
4 | MLIRIR MLIRParser MLIRSupport
5 | MLIROptLib # 对应 #include "mlir/Tools/mlir-opt/MlirOptMain.h"
6 | MLIRFuncDialect # 对应 #include "mlir/Dialect/Func/IR/FuncOps.h"
7 | MLIRTransforms # 对应 #include "mlir/Transforms/Passes.h"
8 | )
--------------------------------------------------------------------------------
/ex3-dialect/tools/toy-opt/toy-opt.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/IR/DialectRegistry.h"
2 | #include "mlir/Tools/mlir-opt/MlirOptMain.h"
3 | // 导入 Func Dialect
4 | #include "mlir/Dialect/Func/IR/FuncOps.h"
5 | // 导入 MLIR 自带 Pass
6 | #include "mlir/Transforms/Passes.h"
7 | // 导入我们新建的 Dialect
8 | #include "toy/ToyDialect.h"
9 | using namespace mlir;
10 | using namespace llvm;
11 |
12 | int main(int argc, char ** argv) {
13 | DialectRegistry registry;
14 | // 注册 Dialect
15 | registry.insert();
16 | // 注册两个 Pass
17 | registerCSEPass();
18 | registerCanonicalizerPass();
19 | return asMainReturnCode(MlirOptMain(argc, argv, "toy-opt", registry));
20 | }
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR})
4 |
5 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
6 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
7 | add_subdirectory(include/toy)
8 | add_subdirectory(lib)
9 | add_subdirectory(tools/toy-opt)
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/ex4.mlir:
--------------------------------------------------------------------------------
1 | toy.func @add(%a: i32, %b: i32) -> i32 {
2 | %c = toy.add %a, %b : i32
3 | toy.ret %c : i32
4 | }
5 |
6 | toy.func @test(%a: i32, %b: i32) -> i32 {
7 | %c = toy.call @add(%a, %b) : (i32, i32) -> i32
8 | toy.ret %c : i32
9 | }
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/include/toy/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_dialect(Toy toy)
2 | add_custom_target(header DEPENDS MLIRToyIncGen)
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/include/toy/Toy.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_TD
2 | #define TOY_TD
3 |
4 | include "toy/ToyDialect.td"
5 | include "toy/ToyOps.td"
6 |
7 | #endif
8 |
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/include/toy/ToyDialect.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/IR/BuiltinDialect.h"
4 | #include "toy/ToyDialect.h.inc"
5 |
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/include/toy/ToyDialect.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_DIALECT_TD
2 | #define TOY_DIALECT_TD
3 |
4 | include "mlir/IR/OpBase.td"
5 |
6 | def ToyDialect : Dialect {
7 | let name = "toy";
8 | let cppNamespace = "::toy";
9 | let summary = "Toy Dialect";
10 | }
11 |
12 | #endif
13 |
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/include/toy/ToyOps.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/IR/BuiltinOps.h"
4 | #include "mlir/IR/Builders.h"
5 |
6 | #include "toy/ToyDialect.h"
7 | #include "mlir/Interfaces/InferTypeOpInterface.h"
8 | #include "mlir/Interfaces/SideEffectInterfaces.h"
9 | #include "mlir/Interfaces/ControlFlowInterfaces.h"
10 | #include "mlir/Interfaces/FunctionInterfaces.h"
11 | #include "mlir/IR/RegionKindInterface.h"
12 |
13 | #define GET_OP_CLASSES
14 | #include "toy/Toy.h.inc"
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/include/toy/ToyOps.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_OPS_TD
2 | #define TOY_OPS_TD
3 |
4 | include "mlir/IR/OpBase.td"
5 | include "toy/ToyDialect.td"
6 | include "mlir/Interfaces/InferTypeOpInterface.td"
7 | include "mlir/Interfaces/SideEffectInterfaces.td"
8 | include "mlir/Interfaces/FunctionInterfaces.td"
9 | include "mlir/Interfaces/ControlFlowInterfaces.td"
10 | include "mlir/IR/RegionKindInterface.td"
11 |
12 | // mnemonic 指名字
13 | class ToyOp traits = []> :
14 | Op;
15 |
16 | // Pure 是 Trait,表示没有 SideEffect 的纯函数
17 | def AddOp : ToyOp<"add", [Pure, SameOperandsAndResultType]> {
18 | let summary = "add operation";
19 | let arguments = (ins Variadic:$inputs);
20 | let results = (outs AnyInteger:$result);
21 | let assemblyFormat = "$inputs attr-dict `:` type($result)";
22 | }
23 |
24 | def SubOp : ToyOp<"sub", [Pure, SameOperandsAndResultType]> {
25 | let summary = "sub operation";
26 | let arguments = (ins AnyInteger:$lhs, AnyInteger:$rhs);
27 | let results = (outs AnyInteger:$result);
28 | let assemblyFormat = "$lhs `,` $rhs attr-dict `:` type($result)";
29 | }
30 |
31 | def ConstantOp : ToyOp<"const", [Pure, InferTypeOpAdaptor]> {
32 | let summary = "const operation";
33 | let arguments = (ins APIntAttr:$value);
34 | let results = (outs AnyInteger:$result);
35 | let assemblyFormat = "$value attr-dict";
36 | let extraClassDeclaration = [{
37 | int64_t getBitWidth() {
38 | return getResult().getType().getWidth();
39 | }
40 | }];
41 | }
42 |
43 | // 另一种实现
44 | //
45 | // def ConstantOp : ToyOp<"const", [Pure, InferTypeOpInterface]> {
46 | // let summary = "const operation";
47 | // let arguments = (ins APIntAttr:$value);
48 | // let results = (outs AnyInteger:$result);
49 | // let assemblyFormat = "$value attr-dict";
50 | // let extraClassDeclaration = [{
51 | // static mlir::LogicalResult inferReturnTypes(
52 | // mlir::MLIRContext * context,
53 | // std::optional<::mlir::Location> location,
54 | // mlir::ValueRange operands,
55 | // mlir::DictionaryAttr attributes,
56 | // mlir::OpaqueProperties properties,
57 | // mlir::RegionRange regions,
58 | // llvm::SmallVectorImpl<::mlir::Type>& inferredReturnTypes
59 | // );
60 | // }];
61 | // }
62 |
63 | def ReturnOp : ToyOp<"ret", [Terminator, ReturnLike]> {
64 | let summary = "return operation";
65 | let arguments = (ins AnyType:$data);
66 | let assemblyFormat = "$data attr-dict `:` type($data)";
67 | }
68 |
69 | def FuncOp : ToyOp<"func", [
70 | FunctionOpInterface,
71 | /* Symbol, */ /* Symbol 会自动被 FunctionOpInterface 加上 */
72 | /* CallableOpInterface, */ /* CallOpInterface 会自动被 FunctionOpInterface 加上 */
73 | RegionKindInterface]> {
74 | let summary = "function";
75 | let arguments = (ins
76 | SymbolNameAttr:$sym_name,
77 | TypeAttrOf:$function_type,
78 | // FunctionOpInterface 需要两个 Attr 来记录 arg 和 res 的名字
79 | OptionalAttr:$arg_attrs,
80 | OptionalAttr:$res_attrs
81 | );
82 | dag regions = (region AnyRegion:$body);
83 | let hasCustomAssemblyFormat = true;
84 | let extraClassDeclaration = [{
85 | // Method of FunctionOpInterface
86 | mlir::Region * getCallableRegion() {return &getBody();}
87 | // getFunctionType 函数会自动生成
88 | // mlir::FunctionType getFunctionType();
89 |
90 | // Method of CallableOpInterface
91 | llvm::ArrayRef getArgumentTypes() {return getFunctionType().getInputs();}
92 | llvm::ArrayRef getResultTypes() {return getFunctionType().getResults();}
93 |
94 | // Method of RegionKindInterface
95 | static mlir::RegionKind getRegionKind(unsigned idx) { return mlir::RegionKind::SSACFG; }
96 | }];
97 | }
98 |
99 | def CallOp : ToyOp<"call", [CallOpInterface]> {
100 | let summary = "call operation";
101 | let arguments = (ins SymbolRefAttr:$callee, Variadic:$arg_operands);
102 | let results = (outs AnyType:$result);
103 | let assemblyFormat = "$callee `(` $arg_operands `)` attr-dict `:` functional-type($arg_operands, results)";
104 | let extraClassDeclaration = [{
105 | mlir::CallInterfaceCallable getCallableForCallee() { return getCalleeAttr(); }
106 | void setCalleeFromCallable(mlir::CallInterfaceCallable callee) { setCalleeAttr(callee.get()); }
107 | }];
108 | }
109 |
110 | #endif
111 |
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_library(Toy toy.cpp DEPENDS MLIRToyIncGen)
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/lib/toy.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/IR/BuiltinAttributes.h"
2 | #include "mlir/IR/BuiltinTypes.h"
3 | #include "mlir/Interfaces/CallInterfaces.h"
4 | #include "mlir/Support/LogicalResult.h"
5 | #include "mlir/Interfaces/FunctionImplementation.h"
6 | #include "toy/ToyDialect.h"
7 | #include "toy/ToyOps.h"
8 |
9 | #include "toy/ToyDialect.cpp.inc"
10 | #define GET_OP_CLASSES
11 | #include "toy/Toy.cpp.inc"
12 |
13 | using namespace mlir;
14 | using namespace toy;
15 |
16 | void ToyDialect::initialize() {
17 | addOperations<
18 | #define GET_OP_LIST
19 | #include "toy/Toy.cpp.inc"
20 | >();
21 | }
22 |
23 | // mlir::LogicalResult ConstantOp::inferReturnTypes(
24 | // mlir::MLIRContext * context,
25 | // std::optional<::mlir::Location> location,
26 | // mlir::ValueRange operands,
27 | // mlir::DictionaryAttr attributes,
28 | // mlir::OpaqueProperties properties,
29 | // mlir::RegionRange regions,
30 | // llvm::SmallVectorImpl<::mlir::Type>& inferredReturnTypes
31 | // ) {
32 | // ConstantOp::Adaptor adaptor(operands, attributes, properties, regions);
33 | // inferredReturnTypes.push_back(adaptor.getValueAttr().getType());
34 | // return success();
35 | // }
36 |
37 | mlir::LogicalResult ConstantOp::inferReturnTypes(
38 | mlir::MLIRContext * context,
39 | std::optional location,
40 | Adaptor adaptor,
41 | llvm::SmallVectorImpl & inferedReturnType
42 | ) {
43 | inferedReturnType.push_back(adaptor.getValueAttr().getType());
44 | return mlir::success();
45 | }
46 |
47 | mlir::ParseResult FuncOp::parse(::mlir::OpAsmParser &parser, ::mlir::OperationState &result) {
48 | auto buildFuncType = [](auto & builder, auto argTypes, auto results, auto, auto) {
49 | return builder.getFunctionType(argTypes, results);
50 | };
51 | return function_interface_impl::parseFunctionOp(
52 | parser, result, false,
53 | getFunctionTypeAttrName(result.name), buildFuncType,
54 | getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name)
55 | );
56 | }
57 |
58 | void FuncOp::print(mlir::OpAsmPrinter &p) {
59 | // Dispatch to the FunctionOpInterface provided utility method that prints the
60 | // function operation.
61 | mlir::function_interface_impl::printFunctionOp(
62 | p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(),
63 | getArgAttrsAttrName(), getResAttrsAttrName());
64 | }
65 |
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/tools/toy-opt/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_tool(ex4-opt toy-opt.cpp)
2 | target_link_libraries(ex4-opt
3 | PRIVATE Toy
4 | MLIRIR MLIRParser MLIRSupport
5 | MLIROptLib # 对应 #include "mlir/Tools/mlir-opt/MlirOptMain.h"
6 | MLIRFuncDialect # 对应 #include "mlir/Dialect/Func/IR/FuncOps.h"
7 | MLIRTransforms # 对应 #include "mlir/Transforms/Passes.h"
8 | )
--------------------------------------------------------------------------------
/ex4-beautiful-dialect/tools/toy-opt/toy-opt.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/IR/DialectRegistry.h"
2 | #include "mlir/Tools/mlir-opt/MlirOptMain.h"
3 | // 导入 Func Dialect
4 | #include "mlir/Dialect/Func/IR/FuncOps.h"
5 | // 导入 MLIR 自带 Pass
6 | #include "mlir/Transforms/Passes.h"
7 | // 导入我们新建的 Dialect
8 | #include "toy/ToyDialect.h"
9 | using namespace mlir;
10 | using namespace llvm;
11 |
12 | int main(int argc, char ** argv) {
13 | DialectRegistry registry;
14 | // 注册 Dialect
15 | registry.insert();
16 | // 注册两个 Pass
17 | registerCSEPass();
18 | registerCanonicalizerPass();
19 | return asMainReturnCode(MlirOptMain(argc, argv, "toy-opt", registry));
20 | }
21 |
--------------------------------------------------------------------------------
/ex5-pass-new/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.13.4)
2 |
3 | project(toy-build LANGUAGES CXX C)
4 |
5 | set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to conform to")
6 |
7 | message(STATUS "Using MLIRConfig.cmake in: ${MLIR_DIR}")
8 |
9 | find_package(MLIR REQUIRED CONFIG)
10 | list(APPEND CMAKE_MODULE_PATH "${MLIR_CMAKE_DIR}")
11 | list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
12 |
13 | # 输出到bin文件夹
14 | set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/bin)
15 |
16 | include(TableGen)
17 | include(AddLLVM)
18 | include(AddMLIR)
19 | include(HandleLLVMOptions)
20 |
21 | include_directories(${MLIR_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS})
22 | include_directories(${PROJECT_SOURCE_DIR}/include)
23 | include_directories(${PROJECT_BINARY_DIR}/include)
24 | add_subdirectory(include)
25 | add_subdirectory(lib)
26 | add_subdirectory(tools)
27 |
--------------------------------------------------------------------------------
/ex5-pass-new/cmake-cmd.sh:
--------------------------------------------------------------------------------
1 | mkdir build; cd build
2 | # 请根据实际情况修改
3 | export export LLVM_BUILD_DIR=~/llvm-project/build
4 | cmake -G Ninja .. \
5 | -DMLIR_DIR=$LLVM_BUILD_DIR/lib/cmake/mlir \
6 | -DLLVM_ENABLE_ASSERTIONS=ON \
7 | -DCMAKE_BUILD_TYPE=Debug
8 | ninja
9 |
--------------------------------------------------------------------------------
/ex5-pass-new/include/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(toy)
2 |
--------------------------------------------------------------------------------
/ex5-pass-new/include/toy/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(Passes)
--------------------------------------------------------------------------------
/ex5-pass-new/include/toy/Passes/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | set(LLVM_TARGET_DEFINITIONS Passes.td)
2 |
3 | mlir_tablegen(Passes.h.inc -gen-pass-decls -name Toy)
4 | add_public_tablegen_target(MLIRToyPassIncGen)
5 |
6 | add_mlir_doc(Passes ToyPasses ./ -gen-pass-doc)
7 |
--------------------------------------------------------------------------------
/ex5-pass-new/include/toy/Passes/Passes.h:
--------------------------------------------------------------------------------
1 | #ifndef TOY_PASSES_H
2 | #define TOY_PASSES_H
3 |
4 | #include "mlir/Pass/Pass.h"
5 | #include "mlir/IR/BuiltinOps.h"
6 | #include
7 |
8 | namespace mlir {
9 | namespace toy {
10 | std::unique_ptr createToyLoopUnrollPass();
11 | std::unique_ptr createToyMem2IterArgsPass();
12 | } // namespace toy
13 | } // namespace mlir
14 |
15 | namespace mlir {
16 |
17 | #define GEN_PASS_REGISTRATION
18 | #include "toy/Passes/Passes.h.inc"
19 |
20 | } // end namespace mlir
21 |
22 | #endif // TOY_PASSES_H
23 |
--------------------------------------------------------------------------------
/ex5-pass-new/include/toy/Passes/Passes.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_PASSES
2 | #define TOY_PASSES
3 |
4 | include "mlir/Pass/PassBase.td"
5 |
6 | def ToyLoopUnroll : Pass<"toy-loop-unroll", "mlir::ModuleOp"> {
7 | let summary = "Loop unroll";
8 | let constructor = "toy::createToyLoopUnrollPass()";
9 | }
10 |
11 | def ToyMem2IterArgs : Pass<"toy-mem-to-iter-args", "mlir::ModuleOp"> {
12 | let summary = "memref load/store to affine.for iter_args";
13 | let constructor = "toy::createToyMem2IterArgsPass()";
14 | }
15 |
16 | #endif // TOY_PASSES
17 |
--------------------------------------------------------------------------------
/ex5-pass-new/lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(toy)
2 |
--------------------------------------------------------------------------------
/ex5-pass-new/lib/toy/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(Passes)
2 |
--------------------------------------------------------------------------------
/ex5-pass-new/lib/toy/Passes/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_dialect_library(MLIRToyTransforms
2 | ToyLoopUnroll.cpp
3 | ToyIterArgs.cpp
4 |
5 | ADDITIONAL_HEADER_DIRS
6 | ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/Affine
7 |
8 | DEPENDS
9 | MLIRToyPassIncGen
10 |
11 | LINK_LIBS PUBLIC
12 | MLIRAffineDialect
13 | MLIRArithDialect
14 | MLIRAffineUtils
15 | MLIRFuncDialect
16 | MLIRFuncTransforms
17 | MLIRIR
18 | MLIRLLVMDialect
19 | MLIRMemRefDialect
20 | MLIRPass
21 | MLIRTransformUtils
22 | )
23 |
--------------------------------------------------------------------------------
/ex5-pass-new/lib/toy/Passes/PassDetails.h:
--------------------------------------------------------------------------------
1 | #ifndef DIALECT_TOY_TRANSFORMS_PASSDETAILS_H
2 | #define DIALECT_TOY_TRANSFORMS_PASSDETAILS_H
3 |
4 | #include "mlir/Pass/Pass.h"
5 | #include "toy/Passes/Passes.h"
6 |
7 | namespace mlir {
8 | namespace toy {
9 |
10 | #define GEN_PASS_CLASSES
11 | #include "toy/Passes/Passes.h.inc"
12 |
13 | } // namespace toy
14 | } // namespace mlir
15 |
16 | #endif // DIALECT_TOY_TRANSFORMS_PASSDETAILS_H
17 |
--------------------------------------------------------------------------------
/ex5-pass-new/lib/toy/Passes/ToyIterArgs.cpp:
--------------------------------------------------------------------------------
1 | #include "PassDetails.h"
2 |
3 | #include "mlir/IR/Dominance.h"
4 | #include "toy/Passes/Passes.h"
5 | #include "llvm/ADT/SmallVector.h"
6 | #include "llvm/Support/Debug.h"
7 |
8 | #include "mlir/Dialect/Affine/IR/AffineOps.h"
9 |
10 | #define DEBUG_TYPE "toy-iter-args-opt"
11 |
12 | using namespace mlir;
13 |
14 | bool isInCurrentAffineFor(Operation *op, affine::AffineForOp forOp) {
15 | auto *parentOp = op->getParentOp();
16 | auto maybeParentFor = dyn_cast_or_null(parentOp);
17 | return maybeParentFor && maybeParentFor == forOp;
18 | }
19 |
20 | bool areInSameAffineFor(affine::AffineLoadOp load, affine::AffineStoreOp store,
21 | affine::AffineForOp forOp) {
22 | return isInCurrentAffineFor(load, forOp) &&
23 | isInCurrentAffineFor(store, forOp);
24 | }
25 |
26 | bool IsParentOp(Operation *op, Operation *maybeParentOp) {
27 | auto iterOp = op;
28 | // 不断迭代op的getParentOp
29 | while (auto parentOp = iterOp->getParentOp()) {
30 | if (parentOp == maybeParentOp)
31 | return true;
32 | iterOp = parentOp;
33 | }
34 | return false;
35 | }
36 |
37 | bool isDominance(Operation *maybeDominateOp, Operation *op) {
38 | DominanceInfo dom(maybeDominateOp);
39 | // 利用 支配关系判断,执行op前maybeDominateOp一定被执行过,
40 | // properly会判断不在同一个块
41 | return dom.properlyDominates(maybeDominateOp, op);
42 | }
43 |
44 | bool checkloadCanPromote(affine::AffineForOp forOp, affine::AffineLoadOp load,
45 | Operation *&storeInfor) {
46 | Value memref = load.getMemRef();
47 | auto mt = memref.getType().cast();
48 | // 只针对memref为1个元素
49 | if (mt.getShape().size()) {
50 | return false;
51 | }
52 | bool storeInforFlag = false;
53 | // 获取 def-use chains
54 | for (auto *user : memref.getUsers()) {
55 | if (auto store = dyn_cast(user)) {
56 | // for循环内的同级store
57 | if (areInSameAffineFor(load, store, forOp)) {
58 | if (storeInforFlag) {
59 | // 仅允许出现一次store
60 | return false;
61 | }
62 | storeInforFlag = true;
63 | // 检查到达 store 都必须经过 这次load,且不在一个block
64 | if (!isDominance(load, store)) {
65 | return false;
66 | }
67 | storeInfor = store;
68 | } else if (IsParentOp(store, forOp)) {
69 | // for region 内还有其他store,不优化
70 | return false;
71 | }
72 | } else if (auto otherLoad = dyn_cast(user)) {
73 | if (load != otherLoad && IsParentOp(otherLoad, forOp)) {
74 | // for region 内有其他 load,不优化
75 | return false;
76 | }
77 | }
78 | }
79 | // debug 时打印优化的memref
80 | LLVM_DEBUG(llvm::dbgs() << " Can promte to iter_args: " << memref << "\n");
81 |
82 | return true;
83 | }
84 |
85 | // 从 mlir/lib/Conversion/VectorToGPU/VectorToGPU.cpp:1104 复制并做了修改
86 | affine::AffineForOp replaceForOpWithNewSignature(OpBuilder &builder,
87 | affine::AffineForOp loop,
88 | Value iterValue) {
89 | OpBuilder::InsertionGuard g(builder);
90 | builder.setInsertionPoint(loop);
91 |
92 | builder.setInsertionPoint(loop);
93 | SmallVector operands;
94 | llvm::append_range(operands, loop.getRegionIterArgs());
95 | operands.push_back(iterValue);
96 |
97 | auto newLoop = builder.create(
98 | loop.getLoc(), loop.getLowerBoundOperands(), loop.getLowerBoundMap(),
99 | loop.getUpperBoundOperands(), loop.getUpperBoundMap(),
100 | loop.getStepAsInt(), operands);
101 | newLoop.getBody()->erase();
102 |
103 | newLoop.getRegion().getBlocks().splice(
104 | newLoop.getRegion().getBlocks().begin(), loop.getRegion().getBlocks());
105 |
106 | newLoop.getBody()->addArgument(iterValue.getType(), iterValue.getLoc());
107 |
108 | for (auto it : llvm::zip(loop.getResults(), newLoop.getResults().take_front(
109 | loop.getNumResults())))
110 | std::get<0>(it).replaceAllUsesWith(std::get<1>(it));
111 |
112 | LLVM_DEBUG(llvm::dbgs() << "newLoop now: " << newLoop << "\n");
113 | LLVM_DEBUG(llvm::dbgs() << "stripped affine.for: " << loop << "\n");
114 | LLVM_DEBUG(llvm::dbgs() << "erase: " << loop);
115 |
116 | loop->erase();
117 | return newLoop;
118 | }
119 |
120 | void replaceWithNewFor(affine::AffineForOp forOp, Operation *load,
121 | Operation *store) {
122 | OpBuilder builder(forOp);
123 | builder.setInsertionPoint(forOp);
124 | auto movedLoad = builder.clone(*load);
125 | auto newLoop =
126 | replaceForOpWithNewSignature(builder, forOp, movedLoad->getResult(0));
127 |
128 | // update yieldOp
129 | auto forYieldOp =
130 | cast(newLoop.getBody()->getTerminator());
131 | forYieldOp->insertOperands(forYieldOp.getNumOperands(), store->getOperand(0));
132 |
133 | // 重写AffineStoreOp
134 | builder.setInsertionPointAfter(newLoop);
135 | auto affineStore = cast(store);
136 |
137 | // store 循环的返回值
138 | builder.create(
139 | newLoop.getLoc(), newLoop.getResults()[newLoop.getNumResults() - 1],
140 | affineStore.getMemRef(), affineStore.getAffineMap(),
141 | affineStore.getMapOperands());
142 |
143 | // 修改load的值为for的最后一个iter_args
144 | load->getResult(0).replaceAllUsesWith(
145 | newLoop.getBody()->getArgument(newLoop.getBody()->getNumArguments() - 1));
146 | // 删除多余的op
147 | load->erase();
148 | store->erase();
149 | }
150 |
151 | // affine.for %arg1 = 0 to 10 {
152 | // %1 = affine.load %arg0[%arg1]
153 | // %2 = affine.load %alloca[] : memref
154 | // %3 = arith.addf %2, %1
155 | // affine.store %3, %alloca[] : memref
156 | // }
157 | //
158 | // becomes
159 | //
160 | // %0 = affine.load %alloca[] : memref
161 | // %1 = affine.for %arg1 = 0 to 10 iter_args(%arg2 = %0) -> (type) {
162 | // %3 = affine.load %arg0[%arg1]
163 | // %4 = arith.addf %arg2, %3
164 | // affine.yield %4
165 | // }
166 | // affine.store %1, %alloca[] : memref
167 |
168 | namespace {
169 | struct ToyMem2IterArgs : public toy::ToyMem2IterArgsBase {
170 | void runOnOperation() override {
171 | auto moduleOp = getOperation();
172 | // 从for循环入手
173 | moduleOp.walk([&](affine::AffineForOp forOp) {
174 | bool isCanPromote = false;
175 | Operation *canPromotetLoadOp, *canPromoteStoreOp;
176 | forOp->walk([&](affine::AffineLoadOp load) {
177 | if (checkloadCanPromote(forOp, load, canPromoteStoreOp)) {
178 | isCanPromote = true;
179 | canPromotetLoadOp = load;
180 | // 可以优化,结束walk
181 | return WalkResult::interrupt();
182 | }
183 | return WalkResult::advance();
184 | });
185 | if (isCanPromote) {
186 | replaceWithNewFor(forOp, canPromotetLoadOp, canPromoteStoreOp);
187 | }
188 | });
189 | }
190 | };
191 |
192 | } // end anonymous namespace
193 |
194 | namespace mlir {
195 | namespace toy {
196 | std::unique_ptr createToyMem2IterArgsPass() {
197 | return std::make_unique();
198 | }
199 | } // namespace toy
200 | } // namespace mlir
201 |
--------------------------------------------------------------------------------
/ex5-pass-new/lib/toy/Passes/ToyLoopUnroll.cpp:
--------------------------------------------------------------------------------
1 | #include "PassDetails.h"
2 |
3 | #include "toy/Passes/Passes.h"
4 | #include "llvm/Support/Debug.h"
5 |
6 | #include "mlir/Dialect/Affine/IR/AffineOps.h"
7 | #include "mlir/Dialect/Affine/LoopUtils.h"
8 |
9 | #define DEBUG_TYPE "toy-loop-unroll"
10 |
11 | using namespace mlir;
12 |
13 | namespace {
14 | struct ToyLoopUnroll : public toy::ToyLoopUnrollBase {
15 | void runOnOperation() override {
16 | auto moduleOp = getOperation();
17 | moduleOp.walk([&](affine::AffineForOp op) {
18 | (void)loopUnrollJamByFactor(op, 4);
19 | });
20 | }
21 | };
22 |
23 | } // end anonymous namespace
24 |
25 | namespace mlir {
26 | namespace toy {
27 | std::unique_ptr createToyLoopUnrollPass() {
28 | return std::make_unique();
29 | }
30 | } // namespace toy
31 | } // namespace mlir
32 |
--------------------------------------------------------------------------------
/ex5-pass-new/sumArray.mlir:
--------------------------------------------------------------------------------
1 | module attributes {} {
2 | func.func @ArraySum(%arg0: memref<10xf32>) -> f32 attributes {llvm.linkage = #llvm.linkage} {
3 | %cst = arith.constant 0.000000e+00 : f32
4 | %alloca = memref.alloca() : memref
5 | affine.store %cst, %alloca[] : memref
6 | affine.for %arg1 = 0 to 10 {
7 | %1 = affine.load %arg0[%arg1] : memref<10xf32>
8 | %2 = affine.load %alloca[] : memref
9 | %3 = arith.addf %2, %1 : f32
10 | affine.store %3, %alloca[] : memref
11 | }
12 | %0 = affine.load %alloca[] : memref
13 | return %0 : f32
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ex5-pass-new/tools/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(toy-opt)
--------------------------------------------------------------------------------
/ex5-pass-new/tools/toy-opt/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
2 | get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS)
3 | set(LIBS
4 | ${dialect_libs}
5 | ${conversion_libs}
6 | MLIROptLib
7 | )
8 | add_llvm_executable(toy-opt toy-opt.cpp)
9 |
10 | llvm_update_compile_flags(toy-opt)
11 |
12 | target_link_libraries(toy-opt PRIVATE ${LIBS})
13 |
14 | mlir_check_all_link_libraries(toy-opt)
--------------------------------------------------------------------------------
/ex5-pass-new/tools/toy-opt/toy-opt.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/Conversion/Passes.h"
2 | #include "mlir/Dialect/Affine/IR/AffineOps.h"
3 | #include "mlir/Dialect/Arith/IR/Arith.h"
4 | #include "mlir/Dialect/Func/IR/FuncOps.h"
5 | #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
6 | #include "mlir/Dialect/LLVMIR/NVVMDialect.h"
7 | #include "mlir/Dialect/Math/IR/Math.h"
8 | #include "mlir/Dialect/MemRef/IR/MemRef.h"
9 | #include "mlir/Dialect/SCF/IR/SCF.h"
10 | #include "mlir/InitAllPasses.h"
11 | #include "mlir/Pass/PassRegistry.h"
12 | #include "mlir/Tools/mlir-opt/MlirOptMain.h"
13 | #include "mlir/Transforms/Passes.h"
14 |
15 | #include "toy/Passes/Passes.h"
16 |
17 | using namespace mlir;
18 |
19 | int main(int argc, char **argv) {
20 | // 注册 mlir 所有Pass
21 | mlir::registerAllPasses();
22 |
23 | mlir::DialectRegistry registry;
24 | // 插入所需dialect
25 | registry.insert();
26 | registry.insert();
27 | registry.insert();
28 | registry.insert();
29 | registry.insert();
30 | registry.insert();
31 | // 导入Toy的所有Pass
32 | mlir::registerToyPasses();
33 | return mlir::failed(mlir::MlirOptMain(argc, argv, "toy-opt", registry));
34 | }
--------------------------------------------------------------------------------
/ex5-pass/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR})
4 |
5 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
6 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
7 | add_subdirectory(include/toy)
8 | add_subdirectory(lib)
9 | add_subdirectory(tools/toy-opt)
--------------------------------------------------------------------------------
/ex5-pass/ex5.mlir:
--------------------------------------------------------------------------------
1 | toy.func @add(%a: i32, %b: i32) -> i32 {
2 | %c = toy.add %a, %b : i32
3 | toy.ret %c : i32
4 | }
5 |
6 | toy.func @test(%a: i32, %b: i32) -> i32 {
7 | %c = toy.call @add(%a, %b) : (i32, i32) -> i32
8 | %d = toy.add %a, %b : i32
9 | %f = toy.add %d, %d : i32
10 | toy.ret %c : i32
11 | }
--------------------------------------------------------------------------------
/ex5-pass/include/toy/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_dialect(Toy toy)
2 |
3 |
4 | set(LLVM_TARGET_DEFINITIONS ToyPasses.td)
5 | mlir_tablegen(ToyPasses.h.inc -gen-pass-decls)
6 | add_public_tablegen_target(MLIRToyTransformsIncGen)
7 |
8 | add_custom_target(header DEPENDS MLIRToyIncGen MLIRToyTransformsIncGen)
--------------------------------------------------------------------------------
/ex5-pass/include/toy/Toy.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_TD
2 | #define TOY_TD
3 |
4 | include "toy/ToyDialect.td"
5 | include "toy/ToyOps.td"
6 |
7 | #endif
8 |
--------------------------------------------------------------------------------
/ex5-pass/include/toy/ToyDialect.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/IR/BuiltinDialect.h"
4 | #include "toy/ToyDialect.h.inc"
5 |
--------------------------------------------------------------------------------
/ex5-pass/include/toy/ToyDialect.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_DIALECT_TD
2 | #define TOY_DIALECT_TD
3 |
4 | include "mlir/IR/OpBase.td"
5 |
6 | def ToyDialect : Dialect {
7 | let name = "toy";
8 | let cppNamespace = "::toy";
9 | let summary = "Toy Dialect";
10 | }
11 |
12 | #endif
13 |
--------------------------------------------------------------------------------
/ex5-pass/include/toy/ToyOps.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/IR/BuiltinOps.h"
4 | #include "mlir/IR/Builders.h"
5 |
6 | #include "toy/ToyDialect.h"
7 | #include "mlir/Interfaces/InferTypeOpInterface.h"
8 | #include "mlir/Interfaces/SideEffectInterfaces.h"
9 | #include "mlir/Interfaces/ControlFlowInterfaces.h"
10 | #include "mlir/Interfaces/FunctionInterfaces.h"
11 | #include "mlir/IR/RegionKindInterface.h"
12 |
13 | #define GET_OP_CLASSES
14 | #include "toy/Toy.h.inc"
--------------------------------------------------------------------------------
/ex5-pass/include/toy/ToyOps.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_OPS_TD
2 | #define TOY_OPS_TD
3 |
4 | include "mlir/IR/OpBase.td"
5 | include "toy/ToyDialect.td"
6 | include "mlir/Interfaces/InferTypeOpInterface.td"
7 | include "mlir/Interfaces/SideEffectInterfaces.td"
8 | include "mlir/Interfaces/FunctionInterfaces.td"
9 | include "mlir/Interfaces/ControlFlowInterfaces.td"
10 | include "mlir/IR/RegionKindInterface.td"
11 |
12 | // mnemonic 指名字
13 | class ToyOp traits = []> :
14 | Op;
15 |
16 | // Pure 是 Trait,表示没有 SideEffect 的纯函数
17 | def AddOp : ToyOp<"add", [Pure, SameOperandsAndResultType]> {
18 | let summary = "add operation";
19 | let arguments = (ins Variadic:$inputs);
20 | let results = (outs AnyInteger:$result);
21 | let assemblyFormat = "$inputs attr-dict `:` type($result)";
22 | }
23 |
24 | def SubOp : ToyOp<"sub", [Pure, SameOperandsAndResultType]> {
25 | let summary = "sub operation";
26 | let arguments = (ins AnyInteger:$lhs, AnyInteger:$rhs);
27 | let results = (outs AnyInteger:$result);
28 | let assemblyFormat = "$lhs `,` $rhs attr-dict `:` type($result)";
29 | }
30 |
31 | def ConstantOp : ToyOp<"const", [Pure, InferTypeOpAdaptor]> {
32 | let summary = "const operation";
33 | let arguments = (ins APIntAttr:$value);
34 | let results = (outs AnyInteger:$result);
35 | let assemblyFormat = "$value attr-dict";
36 | let extraClassDeclaration = [{
37 | int64_t getBitWidth() {
38 | return getResult().getType().getWidth();
39 | }
40 | }];
41 | }
42 |
43 | // 另一种实现
44 | //
45 | // def ConstantOp : ToyOp<"const", [Pure, InferTypeOpInterface]> {
46 | // let summary = "const operation";
47 | // let arguments = (ins APIntAttr:$value);
48 | // let results = (outs AnyInteger:$result);
49 | // let assemblyFormat = "$value attr-dict";
50 | // let extraClassDeclaration = [{
51 | // static mlir::LogicalResult inferReturnTypes(
52 | // mlir::MLIRContext * context,
53 | // std::optional<::mlir::Location> location,
54 | // mlir::ValueRange operands,
55 | // mlir::DictionaryAttr attributes,
56 | // mlir::OpaqueProperties properties,
57 | // mlir::RegionRange regions,
58 | // llvm::SmallVectorImpl<::mlir::Type>& inferredReturnTypes
59 | // );
60 | // }];
61 | // }
62 |
63 | def ReturnOp : ToyOp<"ret", [Terminator, ReturnLike]> {
64 | let summary = "return operation";
65 | let arguments = (ins AnyType:$data);
66 | let assemblyFormat = "$data attr-dict `:` type($data)";
67 | }
68 |
69 | def FuncOp : ToyOp<"func", [
70 | IsolatedFromAbove,
71 | FunctionOpInterface,
72 | /* Symbol, */ /* Symbol 会自动被 FunctionOpInterface 加上 */
73 | /* CallableOpInterface, */ /* CallOpInterface 会自动被 FunctionOpInterface 加上 */
74 | RegionKindInterface]> {
75 | let summary = "function";
76 | let arguments = (ins
77 | SymbolNameAttr:$sym_name,
78 | TypeAttrOf:$function_type,
79 | // FunctionOpInterface 需要两个 Attr 来记录 arg 和 res 的名字
80 | OptionalAttr:$arg_attrs,
81 | OptionalAttr:$res_attrs
82 | );
83 | dag regions = (region AnyRegion:$body);
84 | let hasCustomAssemblyFormat = true;
85 | let extraClassDeclaration = [{
86 | // Method of FunctionOpInterface
87 | mlir::Region * getCallableRegion() {return &getBody();}
88 | // getFunctionType 函数会自动生成
89 | // mlir::FunctionType getFunctionType();
90 |
91 | // Method of CallableOpInterface
92 | llvm::ArrayRef getArgumentTypes() {return getFunctionType().getInputs();}
93 | llvm::ArrayRef getResultTypes() {return getFunctionType().getResults();}
94 |
95 | // Method of RegionKindInterface
96 | static mlir::RegionKind getRegionKind(unsigned idx) { return mlir::RegionKind::SSACFG; }
97 | }];
98 | }
99 |
100 | def CallOp : ToyOp<"call", [CallOpInterface]> {
101 | let summary = "call operation";
102 | let arguments = (ins SymbolRefAttr:$callee, Variadic:$arg_operands);
103 | let results = (outs AnyType:$result);
104 | let assemblyFormat = "$callee `(` $arg_operands `)` attr-dict `:` functional-type($arg_operands, results)";
105 | let extraClassDeclaration = [{
106 | mlir::CallInterfaceCallable getCallableForCallee() { return getCalleeAttr(); }
107 | void setCalleeFromCallable(mlir::CallInterfaceCallable callee) { setCalleeAttr(callee.get()); }
108 | }];
109 | }
110 |
111 | #endif
112 |
--------------------------------------------------------------------------------
/ex5-pass/include/toy/ToyPasses.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/Pass/Pass.h"
4 | #include "mlir/Pass/PassRegistry.h"
5 | #include "toy/ToyOps.h"
6 | #include
7 |
8 | namespace toy {
9 |
10 | #define GEN_PASS_DECL
11 | #include "toy/ToyPasses.h.inc"
12 |
13 | std::unique_ptr createConvertToyToArithPass(ConvertToyToArithOptions options={});
14 | std::unique_ptr createDCEPass();
15 |
16 | #define GEN_PASS_REGISTRATION
17 | #include "toy/ToyPasses.h.inc"
18 |
19 | }
--------------------------------------------------------------------------------
/ex5-pass/include/toy/ToyPasses.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_PASSES_TD
2 | #define TOY_PASSES_TD
3 |
4 | include "mlir/Pass/PassBase.td"
5 |
6 | def ConvertToyToArith : Pass<"convert-toy-to-arith"> {
7 | let summary = "Convert Toy To Arith";
8 | let constructor = "toy::createConvertToyToArithPass()";
9 | let options = [
10 | Option<"name", "name", "std::string", "", "help">
11 | ];
12 | }
13 |
14 | def DCE : Pass<"toy-dce", "toy::FuncOp"> {
15 | let summary = "dce";
16 | let constructor = "toy::createDCEPass()";
17 | }
18 | #endif
19 |
--------------------------------------------------------------------------------
/ex5-pass/lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_library(Toy toy.cpp DEPENDS MLIRToyIncGen)
2 | add_subdirectory(Transforms)
--------------------------------------------------------------------------------
/ex5-pass/lib/Transforms/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_library(
2 | ToyTransforms
3 | ConvertToyToArith.cpp
4 | DCE.cpp
5 | DEPENDS
6 | MLIRToyTransformsIncGen
7 | )
--------------------------------------------------------------------------------
/ex5-pass/lib/Transforms/ConvertToyToArith.cpp:
--------------------------------------------------------------------------------
1 | #include "llvm/Support/raw_ostream.h"
2 | #define GEN_PASS_DEF_CONVERTTOYTOARITH
3 | #include "toy/ToyPasses.h"
4 |
5 | struct ConvertToyToArithPass : toy::impl::ConvertToyToArithBase {
6 | using toy::impl::ConvertToyToArithBase::ConvertToyToArithBase;
7 | void runOnOperation() final {
8 | llvm::errs() << "get name: " << name << "\n";
9 | }
10 | };
11 |
12 | std::unique_ptr toy::createConvertToyToArithPass(ConvertToyToArithOptions options) {
13 | return std::make_unique(options);
14 | }
15 |
--------------------------------------------------------------------------------
/ex5-pass/lib/Transforms/DCE.cpp:
--------------------------------------------------------------------------------
1 | #include "llvm/ADT/STLExtras.h"
2 | #include "llvm/ADT/SmallVector.h"
3 | #include "llvm/Support/raw_ostream.h"
4 | #define GEN_PASS_DEF_DCE
5 | #include "toy/ToyPasses.h"
6 |
7 | using namespace mlir;
8 | using namespace llvm;
9 | using namespace toy;
10 |
11 | struct DCEPass : toy::impl::DCEBase {
12 | void visitAll(llvm::DenseSet &visited, Operation * op) {
13 | if(visited.contains(op)) return;
14 | visited.insert(op);
15 | for(auto operand: op->getOperands())
16 | if(auto def = operand.getDefiningOp())
17 | visitAll(visited, def);
18 | }
19 | void runOnOperation() final {
20 | llvm::DenseSet visited;
21 | getOperation()->walk([&](toy::ReturnOp op) {
22 | visitAll(visited, op);
23 | });
24 | llvm::SmallVector opToRemove;
25 | getOperation().walk([&](Operation * op) {
26 | if(op == getOperation()) return;
27 | if(!visited.contains(op)) opToRemove.push_back(op);
28 | });
29 | for(auto v: reverse(opToRemove)) {
30 | v->erase();
31 | }
32 | }
33 | };
34 |
35 | std::unique_ptr toy::createDCEPass() {
36 | return std::make_unique();
37 | }
38 |
--------------------------------------------------------------------------------
/ex5-pass/lib/toy.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/IR/BuiltinAttributes.h"
2 | #include "mlir/IR/BuiltinTypes.h"
3 | #include "mlir/Interfaces/CallInterfaces.h"
4 | #include "mlir/Support/LogicalResult.h"
5 | #include "mlir/Interfaces/FunctionImplementation.h"
6 | #include "toy/ToyDialect.h"
7 | #include "toy/ToyOps.h"
8 |
9 | #include "toy/ToyDialect.cpp.inc"
10 | #define GET_OP_CLASSES
11 | #include "toy/Toy.cpp.inc"
12 |
13 | using namespace mlir;
14 | using namespace toy;
15 |
16 | void ToyDialect::initialize() {
17 | addOperations<
18 | #define GET_OP_LIST
19 | #include "toy/Toy.cpp.inc"
20 | >();
21 | }
22 |
23 | // mlir::LogicalResult ConstantOp::inferReturnTypes(
24 | // mlir::MLIRContext * context,
25 | // std::optional<::mlir::Location> location,
26 | // mlir::ValueRange operands,
27 | // mlir::DictionaryAttr attributes,
28 | // mlir::OpaqueProperties properties,
29 | // mlir::RegionRange regions,
30 | // llvm::SmallVectorImpl<::mlir::Type>& inferredReturnTypes
31 | // ) {
32 | // ConstantOp::Adaptor adaptor(operands, attributes, properties, regions);
33 | // inferredReturnTypes.push_back(adaptor.getValueAttr().getType());
34 | // return success();
35 | // }
36 |
37 | mlir::LogicalResult ConstantOp::inferReturnTypes(
38 | mlir::MLIRContext * context,
39 | std::optional location,
40 | Adaptor adaptor,
41 | llvm::SmallVectorImpl & inferedReturnType
42 | ) {
43 | inferedReturnType.push_back(adaptor.getValueAttr().getType());
44 | return mlir::success();
45 | }
46 |
47 | mlir::ParseResult FuncOp::parse(::mlir::OpAsmParser &parser, ::mlir::OperationState &result) {
48 | auto buildFuncType = [](auto & builder, auto argTypes, auto results, auto, auto) {
49 | return builder.getFunctionType(argTypes, results);
50 | };
51 | return function_interface_impl::parseFunctionOp(
52 | parser, result, false,
53 | getFunctionTypeAttrName(result.name), buildFuncType,
54 | getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name)
55 | );
56 | }
57 |
58 | void FuncOp::print(mlir::OpAsmPrinter &p) {
59 | // Dispatch to the FunctionOpInterface provided utility method that prints the
60 | // function operation.
61 | mlir::function_interface_impl::printFunctionOp(
62 | p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(),
63 | getArgAttrsAttrName(), getResAttrsAttrName());
64 | }
65 |
--------------------------------------------------------------------------------
/ex5-pass/tools/toy-opt/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_tool(ex5-opt toy-opt.cpp)
2 | target_link_libraries(ex5-opt
3 | PRIVATE Toy
4 | MLIRIR MLIRParser MLIRSupport
5 | MLIROptLib # 对应 #include "mlir/Tools/mlir-opt/MlirOptMain.h"
6 | MLIRFuncDialect # 对应 #include "mlir/Dialect/Func/IR/FuncOps.h"
7 | MLIRTransforms # 对应 #include "mlir/Transforms/Passes.h"
8 | ToyTransforms
9 | )
--------------------------------------------------------------------------------
/ex5-pass/tools/toy-opt/toy-opt.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/IR/DialectRegistry.h"
2 | #include "mlir/Tools/mlir-opt/MlirOptMain.h"
3 | // 导入 Func Dialect
4 | #include "mlir/Dialect/Func/IR/FuncOps.h"
5 | // 导入 MLIR 自带 Pass
6 | #include "mlir/Transforms/Passes.h"
7 | // 导入我们新建的 Dialect
8 | #include "toy/ToyDialect.h"
9 | #include "toy/ToyPasses.h"
10 | using namespace mlir;
11 | using namespace llvm;
12 |
13 | int main(int argc, char ** argv) {
14 | DialectRegistry registry;
15 | // 注册 Dialect
16 | registry.insert();
17 | // 注册两个 Pass
18 | registerCSEPass();
19 | registerCanonicalizerPass();
20 | toy::registerPasses();
21 | return asMainReturnCode(MlirOptMain(argc, argv, "toy-opt", registry));
22 | }
23 |
--------------------------------------------------------------------------------
/ex6-pattern/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR})
4 |
5 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
6 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
7 | add_subdirectory(include/toy)
8 | add_subdirectory(lib)
9 | add_subdirectory(tools/toy-opt)
--------------------------------------------------------------------------------
/ex6-pattern/ex6.mlir:
--------------------------------------------------------------------------------
1 | toy.func @add(%a: i32, %b: i32) -> i32 {
2 | %c = toy.add %a, %b : i32
3 | toy.ret %c : i32
4 | }
5 |
6 | toy.func @test(%a: i32, %b: i32) -> i32 {
7 | %c = toy.call @add(%a, %b) : (i32, i32) -> i32
8 | %d = toy.add %a, %b : i32
9 | %f = toy.add %d, %d : i32
10 | toy.ret %f : i32
11 | }
--------------------------------------------------------------------------------
/ex6-pattern/include/toy/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_dialect(Toy toy)
2 |
3 |
4 | set(LLVM_TARGET_DEFINITIONS ToyPasses.td)
5 | mlir_tablegen(ToyPasses.h.inc -gen-pass-decls)
6 | add_public_tablegen_target(MLIRToyTransformsIncGen)
7 |
8 | add_custom_target(header DEPENDS MLIRToyIncGen MLIRToyTransformsIncGen)
--------------------------------------------------------------------------------
/ex6-pattern/include/toy/Toy.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_TD
2 | #define TOY_TD
3 |
4 | include "toy/ToyDialect.td"
5 | include "toy/ToyOps.td"
6 |
7 | #endif
8 |
--------------------------------------------------------------------------------
/ex6-pattern/include/toy/ToyDialect.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/IR/BuiltinDialect.h"
4 | #include "toy/ToyDialect.h.inc"
5 |
--------------------------------------------------------------------------------
/ex6-pattern/include/toy/ToyDialect.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_DIALECT_TD
2 | #define TOY_DIALECT_TD
3 |
4 | include "mlir/IR/OpBase.td"
5 |
6 | def ToyDialect : Dialect {
7 | let name = "toy";
8 | let cppNamespace = "::toy";
9 | let summary = "Toy Dialect";
10 | }
11 |
12 | #endif
13 |
--------------------------------------------------------------------------------
/ex6-pattern/include/toy/ToyOps.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/IR/BuiltinOps.h"
4 | #include "mlir/IR/Builders.h"
5 |
6 | #include "toy/ToyDialect.h"
7 | #include "mlir/Interfaces/InferTypeOpInterface.h"
8 | #include "mlir/Interfaces/SideEffectInterfaces.h"
9 | #include "mlir/Interfaces/ControlFlowInterfaces.h"
10 | #include "mlir/Interfaces/FunctionInterfaces.h"
11 | #include "mlir/IR/RegionKindInterface.h"
12 |
13 | #define GET_OP_CLASSES
14 | #include "toy/Toy.h.inc"
--------------------------------------------------------------------------------
/ex6-pattern/include/toy/ToyOps.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_OPS_TD
2 | #define TOY_OPS_TD
3 |
4 | include "mlir/IR/OpBase.td"
5 | include "toy/ToyDialect.td"
6 | include "mlir/Interfaces/InferTypeOpInterface.td"
7 | include "mlir/Interfaces/SideEffectInterfaces.td"
8 | include "mlir/Interfaces/FunctionInterfaces.td"
9 | include "mlir/Interfaces/ControlFlowInterfaces.td"
10 | include "mlir/IR/RegionKindInterface.td"
11 |
12 | // mnemonic 指名字
13 | class ToyOp traits = []> :
14 | Op;
15 |
16 | // Pure 是 Trait,表示没有 SideEffect 的纯函数
17 | def AddOp : ToyOp<"add", [Pure, SameOperandsAndResultType]> {
18 | let summary = "add operation";
19 | let arguments = (ins Variadic:$inputs);
20 | let results = (outs AnyInteger:$result);
21 | let assemblyFormat = "$inputs attr-dict `:` type($result)";
22 | }
23 |
24 | def SubOp : ToyOp<"sub", [Pure, SameOperandsAndResultType]> {
25 | let summary = "sub operation";
26 | let arguments = (ins AnyInteger:$lhs, AnyInteger:$rhs);
27 | let results = (outs AnyInteger:$result);
28 | let assemblyFormat = "$lhs `,` $rhs attr-dict `:` type($result)";
29 | }
30 |
31 | def ConstantOp : ToyOp<"const", [Pure, InferTypeOpAdaptor]> {
32 | let summary = "const operation";
33 | let arguments = (ins APIntAttr:$value);
34 | let results = (outs AnyInteger:$result);
35 | let assemblyFormat = "$value attr-dict";
36 | let extraClassDeclaration = [{
37 | int64_t getBitWidth() {
38 | return getResult().getType().getWidth();
39 | }
40 | }];
41 | }
42 |
43 | // 另一种实现
44 | //
45 | // def ConstantOp : ToyOp<"const", [Pure, InferTypeOpInterface]> {
46 | // let summary = "const operation";
47 | // let arguments = (ins APIntAttr:$value);
48 | // let results = (outs AnyInteger:$result);
49 | // let assemblyFormat = "$value attr-dict";
50 | // let extraClassDeclaration = [{
51 | // static mlir::LogicalResult inferReturnTypes(
52 | // mlir::MLIRContext * context,
53 | // std::optional<::mlir::Location> location,
54 | // mlir::ValueRange operands,
55 | // mlir::DictionaryAttr attributes,
56 | // mlir::OpaqueProperties properties,
57 | // mlir::RegionRange regions,
58 | // llvm::SmallVectorImpl<::mlir::Type>& inferredReturnTypes
59 | // );
60 | // }];
61 | // }
62 |
63 | def ReturnOp : ToyOp<"ret", [Terminator, ReturnLike]> {
64 | let summary = "return operation";
65 | let arguments = (ins AnyType:$data);
66 | let assemblyFormat = "$data attr-dict `:` type($data)";
67 | }
68 |
69 | def FuncOp : ToyOp<"func", [
70 | IsolatedFromAbove,
71 | FunctionOpInterface,
72 | /* Symbol, */ /* Symbol 会自动被 FunctionOpInterface 加上 */
73 | /* CallableOpInterface, */ /* CallOpInterface 会自动被 FunctionOpInterface 加上 */
74 | RegionKindInterface]> {
75 | let summary = "function";
76 | let arguments = (ins
77 | SymbolNameAttr:$sym_name,
78 | TypeAttrOf:$function_type,
79 | // FunctionOpInterface 需要两个 Attr 来记录 arg 和 res 的名字
80 | OptionalAttr:$arg_attrs,
81 | OptionalAttr:$res_attrs
82 | );
83 | dag regions = (region AnyRegion:$body);
84 | let hasCustomAssemblyFormat = true;
85 | let extraClassDeclaration = [{
86 | // Method of FunctionOpInterface
87 | mlir::Region * getCallableRegion() {return &getBody();}
88 | // getFunctionType 函数会自动生成
89 | // mlir::FunctionType getFunctionType();
90 |
91 | // Method of CallableOpInterface
92 | llvm::ArrayRef getArgumentTypes() {return getFunctionType().getInputs();}
93 | llvm::ArrayRef getResultTypes() {return getFunctionType().getResults();}
94 |
95 | // Method of RegionKindInterface
96 | static mlir::RegionKind getRegionKind(unsigned idx) { return mlir::RegionKind::SSACFG; }
97 | }];
98 | }
99 |
100 | def CallOp : ToyOp<"call", [CallOpInterface]> {
101 | let summary = "call operation";
102 | let arguments = (ins SymbolRefAttr:$callee, Variadic:$arg_operands);
103 | let results = (outs AnyType:$result);
104 | let assemblyFormat = "$callee `(` $arg_operands `)` attr-dict `:` functional-type($arg_operands, results)";
105 | let extraClassDeclaration = [{
106 | mlir::CallInterfaceCallable getCallableForCallee() { return getCalleeAttr(); }
107 | void setCalleeFromCallable(mlir::CallInterfaceCallable callee) { setCalleeAttr(callee.get()); }
108 | }];
109 | }
110 |
111 | #endif
112 |
--------------------------------------------------------------------------------
/ex6-pattern/include/toy/ToyPasses.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/Pass/Pass.h"
4 | #include "mlir/Pass/PassRegistry.h"
5 | #include "toy/ToyOps.h"
6 | #include
7 |
8 | namespace toy {
9 |
10 | #define GEN_PASS_DECL
11 | #include "toy/ToyPasses.h.inc"
12 |
13 | std::unique_ptr createConvertToyToArithPass(ConvertToyToArithOptions options={});
14 | std::unique_ptr createDCEPass();
15 |
16 | #define GEN_PASS_REGISTRATION
17 | #include "toy/ToyPasses.h.inc"
18 |
19 | }
--------------------------------------------------------------------------------
/ex6-pattern/include/toy/ToyPasses.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_PASSES_TD
2 | #define TOY_PASSES_TD
3 |
4 | include "mlir/Pass/PassBase.td"
5 |
6 | def ConvertToyToArith : Pass<"convert-toy-to-arith"> {
7 | let summary = "Convert Toy To Arith";
8 | let constructor = "toy::createConvertToyToArithPass()";
9 | let options = [
10 | Option<"name", "name", "std::string", "", "help">
11 | ];
12 | }
13 |
14 | def DCE : Pass<"toy-dce", "toy::FuncOp"> {
15 | let summary = "dce";
16 | let constructor = "toy::createDCEPass()";
17 | }
18 | #endif
19 |
--------------------------------------------------------------------------------
/ex6-pattern/lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_library(Toy toy.cpp DEPENDS MLIRToyIncGen)
2 | add_subdirectory(Transforms)
--------------------------------------------------------------------------------
/ex6-pattern/lib/Transforms/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_library(
2 | ToyTransforms
3 | ConvertToyToArith.cpp
4 | DCE.cpp
5 |
6 | DEPENDS MLIRToyTransformsIncGen
7 |
8 | LINK_LIBS MLIRArithDialect
9 | MLIRArithToLLVM
10 | # MLIRLLVMDialect
11 | )
--------------------------------------------------------------------------------
/ex6-pattern/lib/Transforms/ConvertToyToArith.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/IR/BuiltinDialect.h"
2 | #include "mlir/IR/PatternMatch.h"
3 | #include "mlir/Support/LogicalResult.h"
4 | #include "toy/ToyDialect.h"
5 | #include "toy/ToyOps.h"
6 | #include "mlir/Dialect/Arith/IR/Arith.h"
7 | // #include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
8 | // #include "mlir/Conversion/LLVMCommon/TypeConverter.h"
9 | // #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
10 | #include "llvm/ADT/SmallVector.h"
11 | #include "llvm/Support/raw_ostream.h"
12 | #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
13 | #define GEN_PASS_DEF_CONVERTTOYTOARITH
14 | #include "toy/ToyPasses.h"
15 |
16 | #include "mlir/Transforms/DialectConversion.h"
17 |
18 | using namespace mlir;
19 | using namespace llvm;
20 | using namespace toy;
21 |
22 | struct AddOpPat: OpRewritePattern {
23 | using OpRewritePattern::OpRewritePattern;
24 | LogicalResult matchAndRewrite(AddOp op, PatternRewriter & rewriter) const {
25 | auto inputs = to_vector(op.getInputs());
26 | auto result = inputs[0];
27 | for(size_t i = 1; i< inputs.size(); i++) {
28 | result = rewriter.create(op->getLoc(), result, inputs[i]);
29 | }
30 | rewriter.replaceOp(op, ValueRange(result));
31 | return success();
32 | }
33 | };
34 |
35 | struct SubOpPat: OpRewritePattern {
36 | using OpRewritePattern::OpRewritePattern;
37 | LogicalResult matchAndRewrite(SubOp op, PatternRewriter & rewriter) const {
38 | rewriter.replaceOpWithNewOp(op, op.getLhs(), op.getRhs());
39 | return success();
40 | }
41 | };
42 |
43 | struct ConstantOpPat: OpRewritePattern {
44 | using OpRewritePattern::OpRewritePattern;
45 | LogicalResult matchAndRewrite(ConstantOp op, PatternRewriter & rewriter) const {
46 | rewriter.replaceOpWithNewOp(op, op.getValueAttr());
47 | return success();
48 | }
49 | };
50 |
51 | struct ConvertToyToArithPass : toy::impl::ConvertToyToArithBase {
52 | using toy::impl::ConvertToyToArithBase::ConvertToyToArithBase;
53 | void getDependentDialects(DialectRegistry ®istry) const final {
54 | registry.insert();
55 | // registry.insert();
56 | }
57 | void runOnOperation() final {
58 | ConversionTarget target(getContext());
59 | target.addLegalDialect();
60 | // target.addLegalDialect();
61 | // LLVMTypeConverter converter(&getContext());
62 | RewritePatternSet patterns(&getContext());
63 | patterns.add(&getContext());
64 | // arith::populateArithToLLVMConversionPatterns(converter, patterns);
65 | if(failed(applyPartialConversion(getOperation(), target, std::move(patterns))))
66 | signalPassFailure();
67 | }
68 | };
69 |
70 | std::unique_ptr toy::createConvertToyToArithPass(ConvertToyToArithOptions options) {
71 | return std::make_unique(options);
72 | }
73 |
--------------------------------------------------------------------------------
/ex6-pattern/lib/Transforms/DCE.cpp:
--------------------------------------------------------------------------------
1 | #include "llvm/ADT/STLExtras.h"
2 | #include "llvm/ADT/SmallVector.h"
3 | #include "llvm/Support/raw_ostream.h"
4 | #define GEN_PASS_DEF_DCE
5 | #include "toy/ToyPasses.h"
6 |
7 | using namespace mlir;
8 | using namespace llvm;
9 | using namespace toy;
10 |
11 | struct DCEPass : toy::impl::DCEBase {
12 | void visitAll(llvm::DenseSet &visited, Operation * op) {
13 | if(visited.contains(op)) return;
14 | visited.insert(op);
15 | for(auto operand: op->getOperands())
16 | if(auto def = operand.getDefiningOp())
17 | visitAll(visited, def);
18 | }
19 | void runOnOperation() final {
20 | llvm::DenseSet visited;
21 | getOperation()->walk([&](toy::ReturnOp op) {
22 | visitAll(visited, op);
23 | });
24 | llvm::SmallVector opToRemove;
25 | getOperation().walk([&](Operation * op) {
26 | if(op == getOperation()) return;
27 | if(!visited.contains(op)) opToRemove.push_back(op);
28 | });
29 | for(auto v: reverse(opToRemove)) {
30 | v->erase();
31 | }
32 | }
33 | };
34 |
35 | std::unique_ptr toy::createDCEPass() {
36 | return std::make_unique();
37 | }
38 |
--------------------------------------------------------------------------------
/ex6-pattern/lib/toy.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/IR/BuiltinAttributes.h"
2 | #include "mlir/IR/BuiltinTypes.h"
3 | #include "mlir/Interfaces/CallInterfaces.h"
4 | #include "mlir/Support/LogicalResult.h"
5 | #include "mlir/Interfaces/FunctionImplementation.h"
6 | #include "toy/ToyDialect.h"
7 | #include "toy/ToyOps.h"
8 |
9 | #include "toy/ToyDialect.cpp.inc"
10 | #define GET_OP_CLASSES
11 | #include "toy/Toy.cpp.inc"
12 |
13 | using namespace mlir;
14 | using namespace toy;
15 |
16 | void ToyDialect::initialize() {
17 | addOperations<
18 | #define GET_OP_LIST
19 | #include "toy/Toy.cpp.inc"
20 | >();
21 | }
22 |
23 | // mlir::LogicalResult ConstantOp::inferReturnTypes(
24 | // mlir::MLIRContext * context,
25 | // std::optional<::mlir::Location> location,
26 | // mlir::ValueRange operands,
27 | // mlir::DictionaryAttr attributes,
28 | // mlir::OpaqueProperties properties,
29 | // mlir::RegionRange regions,
30 | // llvm::SmallVectorImpl<::mlir::Type>& inferredReturnTypes
31 | // ) {
32 | // ConstantOp::Adaptor adaptor(operands, attributes, properties, regions);
33 | // inferredReturnTypes.push_back(adaptor.getValueAttr().getType());
34 | // return success();
35 | // }
36 |
37 | mlir::LogicalResult ConstantOp::inferReturnTypes(
38 | mlir::MLIRContext * context,
39 | std::optional location,
40 | Adaptor adaptor,
41 | llvm::SmallVectorImpl & inferedReturnType
42 | ) {
43 | inferedReturnType.push_back(adaptor.getValueAttr().getType());
44 | return mlir::success();
45 | }
46 |
47 | mlir::ParseResult FuncOp::parse(::mlir::OpAsmParser &parser, ::mlir::OperationState &result) {
48 | auto buildFuncType = [](auto & builder, auto argTypes, auto results, auto, auto) {
49 | return builder.getFunctionType(argTypes, results);
50 | };
51 | return function_interface_impl::parseFunctionOp(
52 | parser, result, false,
53 | getFunctionTypeAttrName(result.name), buildFuncType,
54 | getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name)
55 | );
56 | }
57 |
58 | void FuncOp::print(mlir::OpAsmPrinter &p) {
59 | // Dispatch to the FunctionOpInterface provided utility method that prints the
60 | // function operation.
61 | mlir::function_interface_impl::printFunctionOp(
62 | p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(),
63 | getArgAttrsAttrName(), getResAttrsAttrName());
64 | }
65 |
--------------------------------------------------------------------------------
/ex6-pattern/tools/toy-opt/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_tool(ex6-opt toy-opt.cpp)
2 | target_link_libraries(ex6-opt
3 | PRIVATE Toy
4 | MLIRIR MLIRParser MLIRSupport
5 | MLIROptLib # 对应 #include "mlir/Tools/mlir-opt/MlirOptMain.h"
6 | MLIRFuncDialect # 对应 #include "mlir/Dialect/Func/IR/FuncOps.h"
7 | MLIRTransforms # 对应 #include "mlir/Transforms/Passes.h"
8 | ToyTransforms
9 | )
--------------------------------------------------------------------------------
/ex6-pattern/tools/toy-opt/toy-opt.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/Dialect/Arith/IR/Arith.h"
2 | #include "mlir/Dialect/LLVMIR/LLVMTypes.h"
3 | #include "mlir/IR/DialectRegistry.h"
4 | #include "mlir/Tools/mlir-opt/MlirOptMain.h"
5 | // #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
6 | // 导入 Func Dialect
7 | #include "mlir/Dialect/Func/IR/FuncOps.h"
8 | // 导入 MLIR 自带 Pass
9 | #include "mlir/Transforms/Passes.h"
10 | // 导入我们新建的 Dialect
11 | #include "toy/ToyDialect.h"
12 | #include "toy/ToyPasses.h"
13 | using namespace mlir;
14 | using namespace llvm;
15 |
16 | int main(int argc, char ** argv) {
17 | DialectRegistry registry;
18 | // 注册 Dialect
19 | registry.insert();
20 | // registry.insert();
21 | // 注册两个 Pass
22 | registerCSEPass();
23 | registerCanonicalizerPass();
24 | toy::registerPasses();
25 | return asMainReturnCode(MlirOptMain(argc, argv, "toy-opt", registry));
26 | }
27 |
--------------------------------------------------------------------------------
/ex7-convert/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR})
4 |
5 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
6 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
7 | add_subdirectory(include/toy)
8 | add_subdirectory(lib)
9 | add_subdirectory(tools/toy-opt)
--------------------------------------------------------------------------------
/ex7-convert/ex7.mlir:
--------------------------------------------------------------------------------
1 | !t32 = !toy.int<32>
2 |
3 | toy.func @add(%a: !t32, %b: !t32) -> !t32 {
4 | %c = toy.add %a, %b : !t32
5 | toy.ret %c : !t32
6 | }
7 |
8 | toy.func @test(%a: !t32, %b: !t32) -> !t32 {
9 | %c = toy.call @add(%a, %b) : (!t32, !t32) -> !t32
10 | %d = toy.add %a, %b : !t32
11 | %f = toy.add %d, %d : !t32
12 | toy.ret %f : !t32
13 | }
--------------------------------------------------------------------------------
/ex7-convert/include/toy/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_dialect(Toy toy)
2 |
3 |
4 | set(LLVM_TARGET_DEFINITIONS ToyPasses.td)
5 | mlir_tablegen(ToyPasses.h.inc -gen-pass-decls)
6 | add_public_tablegen_target(MLIRToyTransformsIncGen)
7 |
8 | add_custom_target(header DEPENDS MLIRToyIncGen MLIRToyTransformsIncGen)
--------------------------------------------------------------------------------
/ex7-convert/include/toy/Toy.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_TD
2 | #define TOY_TD
3 |
4 | include "toy/ToyDialect.td"
5 | include "toy/ToyOps.td"
6 | include "toy/ToyTypes.td"
7 |
8 | #endif
9 |
--------------------------------------------------------------------------------
/ex7-convert/include/toy/ToyDialect.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/IR/BuiltinDialect.h"
4 | #include "toy/ToyDialect.h.inc"
5 |
--------------------------------------------------------------------------------
/ex7-convert/include/toy/ToyDialect.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_DIALECT_TD
2 | #define TOY_DIALECT_TD
3 |
4 | include "mlir/IR/OpBase.td"
5 |
6 | def ToyDialect : Dialect {
7 | let name = "toy";
8 | let cppNamespace = "::toy";
9 | let summary = "Toy Dialect";
10 | let useDefaultTypePrinterParser = true;
11 | let extraClassDeclaration = [{
12 | void registerTypes();
13 | }];
14 | }
15 |
16 | #endif
17 |
--------------------------------------------------------------------------------
/ex7-convert/include/toy/ToyOps.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/IR/BuiltinOps.h"
4 | #include "mlir/IR/Builders.h"
5 |
6 | #include "toy/ToyDialect.h"
7 | #include "toy/ToyTypes.h"
8 | #include "mlir/Interfaces/InferTypeOpInterface.h"
9 | #include "mlir/Interfaces/SideEffectInterfaces.h"
10 | #include "mlir/Interfaces/ControlFlowInterfaces.h"
11 | #include "mlir/Interfaces/FunctionInterfaces.h"
12 | #include "mlir/IR/RegionKindInterface.h"
13 |
14 | #define GET_OP_CLASSES
15 | #include "toy/Toy.h.inc"
--------------------------------------------------------------------------------
/ex7-convert/include/toy/ToyOps.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_OPS_TD
2 | #define TOY_OPS_TD
3 |
4 | include "mlir/IR/OpBase.td"
5 | include "toy/ToyDialect.td"
6 | include "toy/ToyTypes.td"
7 | include "mlir/Interfaces/InferTypeOpInterface.td"
8 | include "mlir/Interfaces/SideEffectInterfaces.td"
9 | include "mlir/Interfaces/FunctionInterfaces.td"
10 | include "mlir/Interfaces/ControlFlowInterfaces.td"
11 | include "mlir/IR/RegionKindInterface.td"
12 |
13 | // mnemonic 指名字
14 | class ToyOp traits = []> :
15 | Op;
16 |
17 | // Pure 是 Trait,表示没有 SideEffect 的纯函数
18 | def AddOp : ToyOp<"add", [Pure, SameOperandsAndResultType]> {
19 | let summary = "add operation";
20 | let arguments = (ins Variadic:$inputs);
21 | let results = (outs ToyInteger:$result);
22 | let assemblyFormat = "$inputs attr-dict `:` type($result)";
23 | }
24 |
25 | def SubOp : ToyOp<"sub", [Pure, SameOperandsAndResultType]> {
26 | let summary = "sub operation";
27 | let arguments = (ins ToyInteger:$lhs, ToyInteger:$rhs);
28 | let results = (outs ToyInteger:$result);
29 | let assemblyFormat = "$lhs `,` $rhs attr-dict `:` type($result)";
30 | }
31 |
32 | def ConstantOp : ToyOp<"const", [Pure, InferTypeOpAdaptor]> {
33 | let summary = "const operation";
34 | let arguments = (ins APIntAttr:$value);
35 | let results = (outs ToyInteger:$result);
36 | let assemblyFormat = "$value attr-dict";
37 | let extraClassDeclaration = [{
38 | int64_t getBitWidth() {
39 | return getResult().getType().getWidth();
40 | }
41 | }];
42 | }
43 |
44 | // 另一种实现
45 | //
46 | // def ConstantOp : ToyOp<"const", [Pure, InferTypeOpInterface]> {
47 | // let summary = "const operation";
48 | // let arguments = (ins APIntAttr:$value);
49 | // let results = (outs AnyInteger:$result);
50 | // let assemblyFormat = "$value attr-dict";
51 | // let extraClassDeclaration = [{
52 | // static mlir::LogicalResult inferReturnTypes(
53 | // mlir::MLIRContext * context,
54 | // std::optional<::mlir::Location> location,
55 | // mlir::ValueRange operands,
56 | // mlir::DictionaryAttr attributes,
57 | // mlir::OpaqueProperties properties,
58 | // mlir::RegionRange regions,
59 | // llvm::SmallVectorImpl<::mlir::Type>& inferredReturnTypes
60 | // );
61 | // }];
62 | // }
63 |
64 | def ReturnOp : ToyOp<"ret", [Terminator, ReturnLike]> {
65 | let summary = "return operation";
66 | let arguments = (ins AnyType:$data);
67 | let assemblyFormat = "$data attr-dict `:` type($data)";
68 | }
69 |
70 | def FuncOp : ToyOp<"func", [
71 | IsolatedFromAbove,
72 | FunctionOpInterface,
73 | /* Symbol, */ /* Symbol 会自动被 FunctionOpInterface 加上 */
74 | /* CallableOpInterface, */ /* CallOpInterface 会自动被 FunctionOpInterface 加上 */
75 | RegionKindInterface]> {
76 | let summary = "function";
77 | let arguments = (ins
78 | SymbolNameAttr:$sym_name,
79 | TypeAttrOf:$function_type,
80 | // FunctionOpInterface 需要两个 Attr 来记录 arg 和 res 的名字
81 | OptionalAttr:$arg_attrs,
82 | OptionalAttr:$res_attrs
83 | );
84 | dag regions = (region AnyRegion:$body);
85 | let hasCustomAssemblyFormat = true;
86 | let extraClassDeclaration = [{
87 | // Method of FunctionOpInterface
88 | mlir::Region * getCallableRegion() {return &getBody();}
89 | // getFunctionType 函数会自动生成
90 | // mlir::FunctionType getFunctionType();
91 |
92 | // Method of CallableOpInterface
93 | llvm::ArrayRef getArgumentTypes() {return getFunctionType().getInputs();}
94 | llvm::ArrayRef getResultTypes() {return getFunctionType().getResults();}
95 |
96 | // Method of RegionKindInterface
97 | static mlir::RegionKind getRegionKind(unsigned idx) { return mlir::RegionKind::SSACFG; }
98 | }];
99 | }
100 |
101 | def CallOp : ToyOp<"call", [CallOpInterface]> {
102 | let summary = "call operation";
103 | let arguments = (ins SymbolRefAttr:$callee, Variadic:$arg_operands);
104 | let results = (outs AnyType:$result);
105 | let assemblyFormat = "$callee `(` $arg_operands `)` attr-dict `:` functional-type($arg_operands, results)";
106 | let extraClassDeclaration = [{
107 | mlir::CallInterfaceCallable getCallableForCallee() { return getCalleeAttr(); }
108 | void setCalleeFromCallable(mlir::CallInterfaceCallable callee) { setCalleeAttr(callee.get()); }
109 | }];
110 | }
111 |
112 | #endif
113 |
--------------------------------------------------------------------------------
/ex7-convert/include/toy/ToyPasses.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mlir/Pass/Pass.h"
4 | #include "mlir/Pass/PassRegistry.h"
5 | #include "toy/ToyOps.h"
6 | #include
7 |
8 | namespace toy {
9 |
10 | #define GEN_PASS_DECL
11 | #include "toy/ToyPasses.h.inc"
12 |
13 | std::unique_ptr createConvertToyToArithPass(ConvertToyToArithOptions options={});
14 | std::unique_ptr createDCEPass();
15 |
16 | #define GEN_PASS_REGISTRATION
17 | #include "toy/ToyPasses.h.inc"
18 |
19 | }
--------------------------------------------------------------------------------
/ex7-convert/include/toy/ToyPasses.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_PASSES_TD
2 | #define TOY_PASSES_TD
3 |
4 | include "mlir/Pass/PassBase.td"
5 |
6 | def ConvertToyToArith : Pass<"convert-toy-to-arith"> {
7 | let summary = "Convert Toy To Arith";
8 | let constructor = "toy::createConvertToyToArithPass()";
9 | let options = [
10 | Option<"name", "name", "std::string", "", "help">
11 | ];
12 | }
13 |
14 | def DCE : Pass<"toy-dce", "toy::FuncOp"> {
15 | let summary = "dce";
16 | let constructor = "toy::createDCEPass()";
17 | }
18 | #endif
19 |
--------------------------------------------------------------------------------
/ex7-convert/include/toy/ToyTypes.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "toy/ToyDialect.h"
4 | #include "mlir/IR/BuiltinTypes.h"
5 | #include "mlir/IR/Types.h"
6 |
7 | #include "llvm/Support/MathExtras.h"
8 |
9 | #define GET_TYPEDEF_CLASSES
10 | #include "toy/ToyTypes.h.inc"
--------------------------------------------------------------------------------
/ex7-convert/include/toy/ToyTypes.td:
--------------------------------------------------------------------------------
1 | #ifndef TOY_TYPES_TD
2 | #define TOY_TYPES_TD
3 |
4 | include "toy/ToyDialect.td"
5 | include "mlir/IR/AttrTypeBase.td"
6 |
7 | class ToyType traits=[]>: TypeDef;
8 |
9 | def ToyInteger: ToyType<"ToyInteger"> {
10 | let mnemonic = "int";
11 | let parameters = (ins "uint64_t":$width);
12 | let assemblyFormat = "`<` $width `>`";
13 | }
14 |
15 | #endif
16 |
--------------------------------------------------------------------------------
/ex7-convert/lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_library(Toy toy.cpp DEPENDS MLIRToyIncGen)
2 | add_subdirectory(Transforms)
--------------------------------------------------------------------------------
/ex7-convert/lib/Transforms/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_mlir_library(
2 | ToyTransforms
3 | ConvertToyToArith.cpp
4 | DCE.cpp
5 |
6 | DEPENDS MLIRToyTransformsIncGen
7 |
8 | LINK_LIBS MLIRArithDialect
9 | MLIRArithToLLVM
10 | # MLIRLLVMDialect
11 | )
--------------------------------------------------------------------------------
/ex7-convert/lib/Transforms/ConvertToyToArith.cpp:
--------------------------------------------------------------------------------
1 | #include "mlir/IR/BuiltinDialect.h"
2 | #include "mlir/IR/BuiltinOps.h"
3 | #include "mlir/IR/BuiltinTypes.h"
4 | #include "mlir/IR/PatternMatch.h"
5 | #include "mlir/Support/LogicalResult.h"
6 | #include "toy/ToyDialect.h"
7 | #include "toy/ToyOps.h"
8 | #include "toy/ToyTypes.h"
9 | #include "mlir/Dialect/Arith/IR/Arith.h"
10 | // #include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
11 | // #include "mlir/Conversion/LLVMCommon/TypeConverter.h"
12 | // #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
13 | #include "llvm/ADT/STLExtras.h"
14 | #include "llvm/ADT/SmallVector.h"
15 | #include "llvm/Support/raw_ostream.h"
16 | #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
17 | #define GEN_PASS_DEF_CONVERTTOYTOARITH
18 | #include "toy/ToyPasses.h"
19 |
20 | #include "mlir/Transforms/DialectConversion.h"
21 |
22 | using namespace mlir;
23 | using namespace llvm;
24 | using namespace toy;
25 |
26 | struct AddOpPat: OpConversionPattern {
27 | using OpConversionPattern::OpConversionPattern;
28 | LogicalResult matchAndRewrite(AddOp op, AddOpAdaptor adaptor, ConversionPatternRewriter & rewriter) const {
29 | auto inputs = to_vector(adaptor.getInputs());
30 | auto result = inputs[0];
31 | for(size_t i = 1; i< inputs.size(); i++) {
32 | assert(inputs[i]);
33 | result = rewriter.create(op->getLoc(), result, inputs[i]);
34 | }
35 | rewriter.replaceOp(op, ValueRange(result));
36 | return success();
37 | }
38 | };
39 |
40 | struct SubOpPat: OpConversionPattern {
41 | using OpConversionPattern::OpConversionPattern;
42 | LogicalResult matchAndRewrite(SubOp op, SubOpAdaptor adaptor, ConversionPatternRewriter & rewriter) const {
43 | rewriter.replaceOpWithNewOp(op, adaptor.getLhs(), adaptor.getRhs());
44 | return success();
45 | }
46 | };
47 |
48 | struct ConstantOpPat: OpConversionPattern {
49 | using OpConversionPattern::OpConversionPattern;
50 | LogicalResult matchAndRewrite(ConstantOp op, ConstantOpAdaptor adaptor, ConversionPatternRewriter & rewriter) const {
51 | rewriter.replaceOpWithNewOp(op, op.getValueAttr());
52 | return success();
53 | }
54 | };
55 |
56 | struct ReturnOpPat: OpConversionPattern {
57 | using OpConversionPattern::OpConversionPattern;
58 | LogicalResult matchAndRewrite(ReturnOp op, ReturnOpAdaptor adaptor, ConversionPatternRewriter & rewriter) const {
59 | auto data = adaptor.getData();
60 | rewriter.startRootUpdate(op);
61 | op.getDataMutable().assign(data);
62 | rewriter.finalizeRootUpdate(op);
63 | return success();
64 | }
65 | };
66 |
67 | struct CallOpPat: OpConversionPattern {
68 | using OpConversionPattern::OpConversionPattern;
69 | LogicalResult matchAndRewrite(CallOp op, CallOpAdaptor adaptor, ConversionPatternRewriter & rewriter) const {
70 | SmallVector resTypes;
71 | assert(succeeded(getTypeConverter()->convertTypes(op->getResultTypes(), resTypes)));
72 | rewriter.replaceOpWithNewOp(op, resTypes, op.getCallee(), adaptor.getOperands());
73 | return success();
74 | }
75 | };
76 |
77 | struct ConvertToyToArithPass : toy::impl::ConvertToyToArithBase {
78 | using toy::impl::ConvertToyToArithBase::ConvertToyToArithBase;
79 | void getDependentDialects(DialectRegistry ®istry) const final {
80 | registry.insert();
81 | }
82 | void runOnOperation() final {
83 | ConversionTarget target(getContext());
84 | target.addLegalDialect();
85 | // target.addDynamicallyLegalOp([](FuncOp f) {
86 | // return llvm::all_of(f.getArgumentTypes(), [](Type t) {return !isa(t);});
87 | // });
88 | auto checkValid = [](Operation* f) {
89 | return llvm::all_of(f->getOperandTypes(), [](Type t) {return !isa(t);});
90 | };
91 | target.addDynamicallyLegalOp(checkValid);
92 | TypeConverter converter;
93 | converter.addConversion([&](ToyIntegerType t) -> std::optional {
94 | return IntegerType::get(&getContext(), t.getWidth());
95 | });
96 | converter.addTargetMaterialization([](OpBuilder& builder, Type resultType, ValueRange inputs, Location loc) -> std::optional {
97 | return builder.create(loc, resultType, inputs).getResult(0);
98 | });
99 | RewritePatternSet patterns(&getContext());
100 | patterns.add(converter, &getContext());
101 | populateFunctionOpInterfaceTypeConversionPattern(patterns, converter);
102 | if(failed(applyPartialConversion(getOperation(), target, std::move(patterns))))
103 | signalPassFailure();
104 | }
105 | };
106 |
107 | std::unique_ptr