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