comparison mlir/docs/Traits.md @ 150:1d019706d866

LLVM10
author anatofuz
date Thu, 13 Feb 2020 15:10:13 +0900
parents
children 0572611fdcc8
comparison
equal deleted inserted replaced
147:c2174574ed3a 150:1d019706d866
1 # Introduction to MLIR Operation Traits
2
3 [TOC]
4
5 MLIR allows for a truly open operation ecosystem, as any dialect may define
6 operations that suit a specific level of abstraction. `Traits` are a mechanism
7 in which to abstract implementation details and properties that are common
8 across many different operations. `Traits` may be used to specify special
9 properties and constraints of the operation, including whether the operation has
10 side effects or whether its output has the same type as the input. Some examples
11 of traits are `Commutative`, `SingleResult`, `Terminator`, etc. See the more
12 [comprehensive list](#traits) below for more examples of what is possible.
13
14 ## Defining a Trait
15
16 Traits may be defined in C++ by inheriting from the
17 `OpTrait::TraitBase<ConcreteType, TraitType>` class. This base class takes as
18 template parameters:
19
20 * ConcreteType
21 - The concrete operation type that this trait was attached to.
22 * TraitType
23 - The type of the trait class that is being defined, for use with the
24 [`Curiously Recurring Template Pattern`](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).
25
26 A derived trait class is expected to take a single template that corresponds to
27 the `ConcreteType`. An example trait definition is shown below:
28
29 ```c++
30 template <typename ConcreteType>
31 class MyTrait : public OpTrait::TraitBase<ConcreteType, MyTrait> {
32 };
33 ```
34
35 Derived traits may also provide a `verifyTrait` hook, that is called when
36 verifying the concrete operation. The trait verifiers will currently always be
37 invoked before the main `Op::verify`.
38
39 ```c++
40 template <typename ConcreteType>
41 class MyTrait : public OpTrait::TraitBase<ConcreteType, MyTrait> {
42 public:
43 /// Override the 'verifyTrait' hook to add additional verification on the
44 /// concrete operation.
45 static LogicalResult verifyTrait(Operation *op) {
46 // ...
47 }
48 };
49 ```
50
51 Note: It is generally good practice to define the implementation of the
52 `verifyTrait` hook out-of-line as a free function when possible to avoid
53 instantiating the implementation for every concrete operation type.
54
55 ### Parametric Traits
56
57 The above demonstrates the definition of a simple self-contained trait. It is
58 also often useful to provide some static parameters to the trait to control its
59 behavior. Given that the definition of the trait class is rigid, i.e. we must
60 have a single template argument for the concrete operation, the templates for
61 the parameters will need to be split out. An example is shown below:
62
63 ```c++
64 template <int Parameter>
65 class MyParametricTrait {
66 public:
67 template <typename ConcreteType>
68 class Impl : public OpTrait::TraitBase<ConcreteType, Impl> {
69 // Inside of 'Impl' we have full access to the template parameters
70 // specified above.
71 };
72 };
73 ```
74
75 ## Attaching a Trait
76
77 Traits may be used when defining a derived operation type, by simply adding the
78 name of the trait class to the `Op` class after the concrete operation type:
79
80 ```c++
81 /// Here we define 'MyOp' along with the 'MyTrait' and `MyParametric trait
82 /// classes we defined previously.
83 class MyOp : public Op<MyOp, MyTrait, MyParametricTrait<10>::Impl> {};
84 ```
85
86 To use a trait in the [ODS](OpDefinitions.md) framework, we need to provide a
87 definition of the trait class. This can be done using the `NativeOpTrait` and
88 `ParamNativeOpTrait` classes. `ParamNativeOpTrait` provides a mechanism in which
89 to specify arguments to a parametric trait class with an internal `Impl`.
90
91 ```tablegen
92 // The argument is the c++ trait class name.
93 def MyTrait : NativeOpTrait<"MyTrait">;
94
95 // The first argument is the parent c++ class name. The second argument is a
96 // string containing the parameter list.
97 class MyParametricTrait<int prop>
98 : NativeOpTrait<"MyParametricTrait", !cast<string>(!head(parameters))>;
99 ```
100
101 These can then be used in the `traits` list of an op definition:
102
103 ```tablegen
104 def OpWithInferTypeInterfaceOp : Op<...[MyTrait, MyParametricTrait<10>]> { ... }
105 ```
106
107 See the documentation on [operation definitions](OpDefinitions.md) for more
108 details.
109
110 ## Using a Trait
111
112 Traits may be used to provide additional methods, static fields, or other
113 information directly on the concrete operation. `Traits` internally become
114 `Base` classes of the concrete operation, so all of these are directly
115 accessible. To expose this information opaquely to transformations and analyses,
116 [`interfaces`](Interfaces.md) may be used.
117
118 To query if a specific operation contains a specific trait, the `hasTrait<>`
119 method may be used. This takes as a template parameter the trait class, which is
120 the same as the one passed when attaching the trait to an operation.
121
122 ```c++
123 Operation *op = ..;
124 if (op->hasTrait<MyTrait>() || op->hasTrait<MyParametricTrait<10>::Impl>())
125 ...;
126 ```
127
128 ## Trait List
129
130 MLIR provides a suite of traits that provide various functionalities that are
131 common across many different operations. Below is a list of some key traits that
132 may be used directly by any dialect. The format of the header for each trait
133 section goes as follows:
134
135 * `Header`
136 - (`C++ class` -- `ODS class`(if applicable))
137
138 ### Broadcastable
139
140 * `OpTrait::ResultsBroadcastableShape` -- `ResultsBroadcastableShape`
141
142 This trait adds the property that the operation is known to have
143 [broadcast-compatible](https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html)
144 operands and its result types' shape is the broadcast compatible with the shape
145 of the broadcasted operands. Specifically, starting from the most varying
146 dimension, each dimension pair of the two operands' shapes should either be the
147 same or one of them is one. Also, the result shape should have the corresponding
148 dimension equal to the larger one, if known. Shapes are checked partially if
149 ranks or dimensions are not known. For example, an op with `tensor<?x2xf32>` and
150 `tensor<2xf32>` as operand types and `tensor<3x2xf32>` as the result type is
151 broadcast-compatible.
152
153 This trait requires that the operands are either vector or tensor types.
154
155 ### Commutative
156
157 * `OpTrait::IsCommutative` -- `Commutative`
158
159 This trait adds the property that the operation is commutative, i.e. `X op Y ==
160 Y op X`
161
162 ### Function-Like
163
164 * `OpTrait::FunctionLike`
165
166 This trait provides APIs for operations that behave like functions. In
167 particular:
168
169 - Ops must be symbols, i.e. also have the `Symbol` trait;
170 - Ops have a single region with multiple blocks that corresponds to the body
171 of the function;
172 - the absence of a region corresponds to an external function;
173 - arguments of the first block of the region are treated as function
174 arguments;
175 - they can have argument and result attributes that are stored in dictionary
176 attributes on the operation itself.
177
178 This trait does *NOT* provide type support for the functions, meaning that
179 concrete Ops must handle the type of the declared or defined function.
180 `getTypeAttrName()` is a convenience function that returns the name of the
181 attribute that can be used to store the function type, but the trait makes no
182 assumption based on it.
183
184 ### HasParent
185
186 * `OpTrait::HasParent<typename ParentOpType>` -- `HasParent<string op>`
187
188 This trait provides APIs and verifiers for operations that can only be nested
189 within regions that are attached to operations of `ParentOpType`.
190
191 ### IsolatedFromAbove
192
193 * `OpTrait::IsIsolatedFromAbove` -- `IsolatedFromAbove`
194
195 This trait signals that the regions of an operations are known to be isolated
196 from above. This trait asserts that the regions of an operation will not
197 capture, or reference, SSA values defined above the region scope. This means
198 that the following is invalid if `foo.region_op` is defined as
199 `IsolatedFromAbove`:
200
201 ```mlir
202 %result = constant 10 : i32
203 foo.region_op {
204 foo.yield %result : i32
205 }
206 ```
207
208 This trait is an important structural property of the IR, and enables operations
209 to have [passes](WritingAPass.md) scheduled under them.
210
211 ### NoSideEffect
212
213 * `OpTrait::HasNoSideEffect` -- `NoSideEffect`
214
215 This trait signifies that the operation is pure and has no visible side effects.
216
217 ### Single Block with Implicit Terminator
218
219 * `OpTrait::SingleBlockImplicitTerminator<typename TerminatorOpType>` :
220 `SingleBlockImplicitTerminator<string op>`
221
222 This trait provides APIs and verifiers for operations with regions that have a
223 single block that must terminate with `TerminatorOpType`.
224
225 ### Symbol
226
227 * `OpTrait::Symbol` -- `Symbol`
228
229 This trait is used for operations that define a
230 [`Symbol`](SymbolsAndSymbolTables.md#symbol).
231
232 ### SymbolTable
233
234 * `OpTrait::SymbolTable` -- `SymbolTable`
235
236 This trait is used for operations that define a
237 [`SymbolTable`](SymbolsAndSymbolTables.md#symbol-table).
238
239 ### Terminator
240
241 * `OpTrait::IsTerminator` -- `Terminator`
242
243 This trait provides verification and functionality for operations that are known
244 to be [terminators](LangRef.md#terminator-operations).