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