5 MLIR allows for a truly open ecosystem, as any dialect may define attributes,
6 operations, and types that suit a specific level of abstraction. `Traits` are a
7 mechanism which abstracts implementation details and properties that are common
8 across many different attributes/operations/types/etc.. `Traits` may be used to
9 specify special properties and constraints of the object, including whether an
10 operation has side effects or that its output has the same type as the input.
11 Some examples of operation traits are `Commutative`, `SingleResult`,
12 `Terminator`, etc. See the more comprehensive list of
13 [operation traits](#operation-traits-list) below for more examples of what is
18 Traits may be defined in C++ by inheriting from the `TraitBase<ConcreteType,
19 TraitType>` class for the specific IR type. For attributes, this is
20 `AttributeTrait::TraitBase`. For operations, this is `OpTrait::TraitBase`. For
21 types, this is `TypeTrait::TraitBase`. This base class takes as template
25 - The concrete class type that this trait was attached to.
27 - The type of the trait class that is being defined, for use with the
28 [`Curiously Recurring Template Pattern`](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).
30 A derived trait class is expected to take a single template that corresponds to
31 the `ConcreteType`. An example trait definition is shown below:
34 template <typename ConcreteType>
35 class MyTrait : public TraitBase<ConcreteType, MyTrait> {
39 Operation traits may also provide a `verifyTrait` or `verifyRegionTrait` hook
40 that is called when verifying the concrete operation. The difference between
41 these two is that whether the verifier needs to access the regions, if so, the
42 operations in the regions will be verified before the verification of this
43 trait. The [verification order](../DefiningDialects/Operations.md/#verification-ordering)
44 determines when a verifier will be invoked.
47 template <typename ConcreteType>
48 class MyTrait : public OpTrait::TraitBase<ConcreteType, MyTrait> {
50 /// Override the 'verifyTrait' hook to add additional verification on the
51 /// concrete operation.
52 static LogicalResult verifyTrait(Operation *op) {
58 Note: It is generally good practice to define the implementation of the
59 `verifyTrait` or `verifyRegionTrait` hook out-of-line as a free function when
60 possible to avoid instantiating the implementation for every concrete operation
63 Operation traits may also provide a `foldTrait` hook that is called when folding
64 the concrete operation. The trait folders will only be invoked if the concrete
65 operation fold is either not implemented, fails, or performs an in-place fold.
67 The following signature of fold will be called if it is implemented and the op
71 template <typename ConcreteType>
72 class MyTrait : public OpTrait::TraitBase<ConcreteType, MyTrait> {
74 /// Override the 'foldTrait' hook to support trait based folding on the
75 /// concrete operation.
76 static OpFoldResult foldTrait(Operation *op, ArrayRef<Attribute> operands) {
82 Otherwise, if the operation has a single result and the above signature is not
83 implemented, or the operation has multiple results, then the following signature
84 will be used (if implemented):
87 template <typename ConcreteType>
88 class MyTrait : public OpTrait::TraitBase<ConcreteType, MyTrait> {
90 /// Override the 'foldTrait' hook to support trait based folding on the
91 /// concrete operation.
92 static LogicalResult foldTrait(Operation *op, ArrayRef<Attribute> operands,
93 SmallVectorImpl<OpFoldResult> &results) {
99 Note: It is generally good practice to define the implementation of the
100 `foldTrait` hook out-of-line as a free function when possible to avoid
101 instantiating the implementation for every concrete operation type.
103 ### Extra Declarations and Definitions
104 A trait may require additional declarations and definitions directly on
105 the Operation, Attribute or Type instances which specify that trait.
106 The `extraConcreteClassDeclaration` and `extraConcreteClassDefinition`
107 fields under the `NativeTrait` class are mechanisms designed for injecting
108 code directly into generated C++ Operation, Attribute or Type classes.
110 Code within the `extraConcreteClassDeclaration` field will be formatted and copied
111 into the generated C++ Operation, Attribute or Type class. Code within
112 `extraConcreteClassDefinition` will be added to the generated source file inside
113 the class’s C++ namespace. The substitution `$cppClass` is replaced by the C++ class
116 The intention is to group trait specific logic together and reduce
117 redundant extra declarations and definitions on the instances themselves.
119 ### Parametric Traits
121 The above demonstrates the definition of a simple self-contained trait. It is
122 also often useful to provide some static parameters to the trait to control its
123 behavior. Given that the definition of the trait class is rigid, i.e. we must
124 have a single template argument for the concrete object, the templates for the
125 parameters will need to be split out. An example is shown below:
128 template <int Parameter>
129 class MyParametricTrait {
131 template <typename ConcreteType>
132 class Impl : public TraitBase<ConcreteType, Impl> {
133 // Inside of 'Impl' we have full access to the template parameters
141 Traits may be used when defining a derived object type, by simply appending the
142 name of the trait class to the end of the base object class operation type:
145 /// Here we define 'MyAttr' along with the 'MyTrait' and `MyParametric trait
146 /// classes we defined previously.
147 class MyAttr : public Attribute::AttrBase<MyAttr, ..., MyTrait, MyParametricTrait<10>::Impl> {};
148 /// Here we define 'MyOp' along with the 'MyTrait' and `MyParametric trait
149 /// classes we defined previously.
150 class MyOp : public Op<MyOp, MyTrait, MyParametricTrait<10>::Impl> {};
151 /// Here we define 'MyType' along with the 'MyTrait' and `MyParametric trait
152 /// classes we defined previously.
153 class MyType : public Type::TypeBase<MyType, ..., MyTrait, MyParametricTrait<10>::Impl> {};
156 ### Attaching Operation Traits in ODS
158 To use an operation trait in the [ODS](../DefiningDialects/Operations.md) framework, we need to
159 provide a definition of the trait class. This can be done using the
160 `NativeOpTrait` and `ParamNativeOpTrait` classes. `ParamNativeOpTrait` provides
161 a mechanism in which to specify arguments to a parametric trait class with an
165 // The argument is the c++ trait class name.
166 def MyTrait : NativeOpTrait<"MyTrait">;
168 // The first argument is the parent c++ class name. The second argument is a
169 // string containing the parameter list.
170 class MyParametricTrait<int prop>
171 : NativeOpTrait<"MyParametricTrait", !cast<string>(!head(parameters))>;
174 These can then be used in the `traits` list of an op definition:
177 def OpWithInferTypeInterfaceOp : Op<...[MyTrait, MyParametricTrait<10>]> { ... }
180 See the documentation on [operation definitions](../DefiningDialects/Operations.md) for more
185 Traits may be used to provide additional methods, static fields, or other
186 information directly on the concrete object. `Traits` internally become `Base`
187 classes of the concrete operation, so all of these are directly accessible. To
188 expose this information opaquely to transformations and analyses,
189 [`interfaces`](../Interfaces.md) may be used.
191 To query if a specific object contains a specific trait, the `hasTrait<>` method
192 may be used. This takes as a template parameter the trait class, which is the
193 same as the one passed when attaching the trait to an operation.
197 if (op->hasTrait<MyTrait>() || op->hasTrait<MyParametricTrait<10>::Impl>())
201 ## Operation Traits List
203 MLIR provides a suite of traits that provide various functionalities that are
204 common across many different operations. Below is a list of some key traits that
205 may be used directly by any dialect. The format of the header for each trait
206 section goes as follows:
209 - (`C++ class` -- `ODS class`(if applicable))
213 * `OpTrait::AffineScope` -- `AffineScope`
215 This trait is carried by region holding operations that define a new scope for
216 the purposes of polyhedral optimization and the affine dialect in particular.
217 Any SSA values of 'index' type that either dominate such operations, or are
218 defined at the top-level of such operations, or appear as region arguments for
219 such operations automatically become valid symbols for the polyhedral scope
220 defined by that operation. As a result, such SSA values could be used as the
221 operands or index operands of various affine dialect operations like affine.for,
222 affine.load, and affine.store. The polyhedral scope defined by an operation with
223 this trait includes all operations in its region excluding operations that are
224 nested inside of other operations that themselves have this trait.
226 ### AutomaticAllocationScope
228 * `OpTrait::AutomaticAllocationScope` -- `AutomaticAllocationScope`
230 This trait is carried by region holding operations that define a new scope for
231 automatic allocation. Such allocations are automatically freed when control is
232 transferred back from the regions of such operations. As an example, allocations
234 [`memref.alloca`](../Dialects/MemRef.md/#memrefalloca-memrefallocaop) are
235 automatically freed when control leaves the region of its closest surrounding op
236 that has the trait AutomaticAllocationScope.
240 * `OpTrait::ResultsBroadcastableShape` -- `ResultsBroadcastableShape`
242 This trait adds the property that the operation is known to have
243 [broadcast-compatible](https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html)
244 operands and that its result type is compatible with the inferred broadcast shape.
245 See [The `Broadcastable` Trait](Broadcastable.md) for details.
249 * `OpTrait::IsCommutative` -- `Commutative`
251 This trait adds the property that the operation is commutative, i.e. `X op Y ==
254 ### ElementwiseMappable
256 * `OpTrait::ElementwiseMappable` -- `ElementwiseMappable`
258 This trait tags scalar ops that also can be applied to vectors/tensors, with
259 their semantics on vectors/tensors being elementwise application. This trait
260 establishes a set of properties that allow reasoning about / converting between
261 scalar/vector/tensor code. These same properties allow blanket implementations
262 of various analyses/transformations for all `ElementwiseMappable` ops.
264 Note: Not all ops that are "elementwise" in some abstract sense satisfy this
265 trait. In particular, broadcasting behavior is not allowed. See the comments on
266 `OpTrait::ElementwiseMappable` for the precise requirements.
270 * `OpTrait::HasParent<typename ParentOpType>` -- `HasParent<string op>` or
271 `ParentOneOf<list<string> opList>`
273 This trait provides APIs and verifiers for operations that can only be nested
274 within regions that are attached to operations of `ParentOpType`.
276 ### IsolatedFromAbove
278 * `OpTrait::IsIsolatedFromAbove` -- `IsolatedFromAbove`
280 This trait signals that the regions of an operations are known to be isolated
281 from above. This trait asserts that the regions of an operation will not
282 capture, or reference, SSA values defined above the region scope. This means
283 that the following is invalid if `foo.region_op` is defined as
287 %result = arith.constant 10 : i32
289 foo.yield %result : i32
293 This trait is an important structural property of the IR, and enables operations
294 to have [passes](../PassManagement) scheduled under them.
296 ### MemRefsNormalizable
298 * `OpTrait::MemRefsNormalizable` -- `MemRefsNormalizable`
300 This trait is used to flag operations that consume or produce values of `MemRef`
301 type where those references can be 'normalized'. In cases where an associated
302 `MemRef` has a non-identity memory-layout specification, such normalizable
303 operations can be modified so that the `MemRef` has an identity layout
304 specification. This can be implemented by associating the operation with its own
305 index expression that can express the equivalent of the memory-layout
306 specification of the MemRef type. See [the -normalize-memrefs pass](../Passes.md/#-normalize-memrefs).
308 ### Single Block Region
310 * `OpTrait::SingleBlock` -- `SingleBlock`
312 This trait provides APIs and verifiers for operations with regions that have a
315 ### Single Block with Implicit Terminator
317 * `OpTrait::SingleBlockImplicitTerminator<typename TerminatorOpType>` --
318 `SingleBlockImplicitTerminator<string op>`
320 This trait implies the `SingleBlock` above, but adds the additional requirement
321 that the single block must terminate with `TerminatorOpType`.
325 * `OpTrait::SymbolTable` -- `SymbolTable`
327 This trait is used for operations that define a
328 [`SymbolTable`](../SymbolsAndSymbolTables.md/#symbol-table).
332 * `OpTrait::IsTerminator` -- `Terminator`
334 This trait provides verification and functionality for operations that are known
335 to be [terminators](../LangRef.md/#control-flow-and-ssacfg-regions).
337 * `OpTrait::NoTerminator` -- `NoTerminator`
339 This trait removes the requirement on regions held by an operation to have
340 [terminator operations](../LangRef.md/#control-flow-and-ssacfg-regions) at the end of a block.
341 This requires that these regions have a single block. An example of operation
342 using this trait is the top-level `ModuleOp`.