173
|
1 # Operation Traits
|
150
|
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
|
173
|
7 which abstracts implementation details and properties that are common
|
150
|
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
|
173
|
138 ### AffineScope
|
|
139
|
|
140 * `OpTrait::AffineScope` -- `AffineScope`
|
|
141
|
|
142 This trait is carried by region holding operations that define a new scope for
|
|
143 the purposes of polyhedral optimization and the affine dialect in particular.
|
|
144 Any SSA values of 'index' type that either dominate such operations, or are
|
|
145 defined at the top-level of such operations, or appear as region arguments for
|
|
146 such operations automatically become valid symbols for the polyhedral scope
|
|
147 defined by that operation. As a result, such SSA values could be used as the
|
|
148 operands or index operands of various affine dialect operations like affine.for,
|
|
149 affine.load, and affine.store. The polyhedral scope defined by an operation
|
|
150 with this trait includes all operations in its region excluding operations that
|
|
151 are nested inside of other operations that themselves have this trait.
|
|
152
|
|
153 ### AutomaticAllocationScope
|
|
154
|
|
155 * `OpTrait::AutomaticAllocationScope` -- `AutomaticAllocationScope`
|
|
156
|
|
157 This trait is carried by region holding operations that define a new scope for
|
|
158 automatic allocation. Such allocations are automatically freed when control is
|
|
159 transferred back from the regions of such operations. As an example, allocations
|
|
160 performed by [`std.alloca`](Dialects/Standard.md#stdalloca-allocaop) are
|
|
161 automatically freed when control leaves the region of its closest surrounding op
|
|
162 that has the trait AutomaticAllocationScope.
|
|
163
|
150
|
164 ### Broadcastable
|
|
165
|
|
166 * `OpTrait::ResultsBroadcastableShape` -- `ResultsBroadcastableShape`
|
|
167
|
|
168 This trait adds the property that the operation is known to have
|
|
169 [broadcast-compatible](https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html)
|
|
170 operands and its result types' shape is the broadcast compatible with the shape
|
|
171 of the broadcasted operands. Specifically, starting from the most varying
|
|
172 dimension, each dimension pair of the two operands' shapes should either be the
|
|
173 same or one of them is one. Also, the result shape should have the corresponding
|
|
174 dimension equal to the larger one, if known. Shapes are checked partially if
|
|
175 ranks or dimensions are not known. For example, an op with `tensor<?x2xf32>` and
|
|
176 `tensor<2xf32>` as operand types and `tensor<3x2xf32>` as the result type is
|
|
177 broadcast-compatible.
|
|
178
|
|
179 This trait requires that the operands are either vector or tensor types.
|
|
180
|
|
181 ### Commutative
|
|
182
|
|
183 * `OpTrait::IsCommutative` -- `Commutative`
|
|
184
|
|
185 This trait adds the property that the operation is commutative, i.e. `X op Y ==
|
|
186 Y op X`
|
|
187
|
|
188 ### Function-Like
|
|
189
|
|
190 * `OpTrait::FunctionLike`
|
|
191
|
|
192 This trait provides APIs for operations that behave like functions. In
|
|
193 particular:
|
|
194
|
|
195 - Ops must be symbols, i.e. also have the `Symbol` trait;
|
|
196 - Ops have a single region with multiple blocks that corresponds to the body
|
|
197 of the function;
|
|
198 - the absence of a region corresponds to an external function;
|
|
199 - arguments of the first block of the region are treated as function
|
|
200 arguments;
|
|
201 - they can have argument and result attributes that are stored in dictionary
|
|
202 attributes on the operation itself.
|
|
203
|
|
204 This trait does *NOT* provide type support for the functions, meaning that
|
|
205 concrete Ops must handle the type of the declared or defined function.
|
|
206 `getTypeAttrName()` is a convenience function that returns the name of the
|
|
207 attribute that can be used to store the function type, but the trait makes no
|
|
208 assumption based on it.
|
|
209
|
|
210 ### HasParent
|
|
211
|
|
212 * `OpTrait::HasParent<typename ParentOpType>` -- `HasParent<string op>`
|
|
213
|
|
214 This trait provides APIs and verifiers for operations that can only be nested
|
|
215 within regions that are attached to operations of `ParentOpType`.
|
|
216
|
|
217 ### IsolatedFromAbove
|
|
218
|
|
219 * `OpTrait::IsIsolatedFromAbove` -- `IsolatedFromAbove`
|
|
220
|
|
221 This trait signals that the regions of an operations are known to be isolated
|
|
222 from above. This trait asserts that the regions of an operation will not
|
|
223 capture, or reference, SSA values defined above the region scope. This means
|
|
224 that the following is invalid if `foo.region_op` is defined as
|
|
225 `IsolatedFromAbove`:
|
|
226
|
|
227 ```mlir
|
|
228 %result = constant 10 : i32
|
|
229 foo.region_op {
|
|
230 foo.yield %result : i32
|
|
231 }
|
|
232 ```
|
|
233
|
|
234 This trait is an important structural property of the IR, and enables operations
|
173
|
235 to have [passes](PassManagement.md) scheduled under them.
|
150
|
236
|
|
237 ### Single Block with Implicit Terminator
|
|
238
|
|
239 * `OpTrait::SingleBlockImplicitTerminator<typename TerminatorOpType>` :
|
|
240 `SingleBlockImplicitTerminator<string op>`
|
|
241
|
|
242 This trait provides APIs and verifiers for operations with regions that have a
|
|
243 single block that must terminate with `TerminatorOpType`.
|
|
244
|
|
245 ### Symbol
|
|
246
|
|
247 * `OpTrait::Symbol` -- `Symbol`
|
|
248
|
|
249 This trait is used for operations that define a
|
|
250 [`Symbol`](SymbolsAndSymbolTables.md#symbol).
|
|
251
|
|
252 ### SymbolTable
|
|
253
|
|
254 * `OpTrait::SymbolTable` -- `SymbolTable`
|
|
255
|
|
256 This trait is used for operations that define a
|
|
257 [`SymbolTable`](SymbolsAndSymbolTables.md#symbol-table).
|
|
258
|
|
259 ### Terminator
|
|
260
|
|
261 * `OpTrait::IsTerminator` -- `Terminator`
|
|
262
|
|
263 This trait provides verification and functionality for operations that are known
|
|
264 to be [terminators](LangRef.md#terminator-operations).
|