2020-07-01 06:42:52 +08:00
|
|
|
# Traits
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
[TOC]
|
|
|
|
|
2020-07-01 06:42:52 +08:00
|
|
|
MLIR allows for a truly open ecosystem, as any dialect may define attributes,
|
|
|
|
operations, and types that suit a specific level of abstraction. `Traits` are a
|
|
|
|
mechanism which abstracts implementation details and properties that are common
|
|
|
|
across many different attributes/operations/types/etc.. `Traits` may be used to
|
|
|
|
specify special properties and constraints of the object, including whether an
|
|
|
|
operation has side effects or that its output has the same type as the input.
|
|
|
|
Some examples of operation traits are `Commutative`, `SingleResult`,
|
|
|
|
`Terminator`, etc. See the more comprehensive list of
|
|
|
|
[operation traits](#operation-traits-list) below for more examples of what is
|
|
|
|
possible.
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
## Defining a Trait
|
|
|
|
|
2020-07-01 06:42:52 +08:00
|
|
|
Traits may be defined in C++ by inheriting from the `TraitBase<ConcreteType,
|
|
|
|
TraitType>` class for the specific IR type. For attributes, this is
|
|
|
|
`AttributeTrait::TraitBase`. For operations, this is `OpTrait::TraitBase`. For
|
|
|
|
types, this is `TypeTrait::TraitBase`. This base class takes as template
|
|
|
|
parameters:
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
* ConcreteType
|
2020-07-01 06:42:52 +08:00
|
|
|
- The concrete class type that this trait was attached to.
|
2019-11-21 10:19:38 +08:00
|
|
|
* TraitType
|
|
|
|
- The type of the trait class that is being defined, for use with the
|
|
|
|
[`Curiously Recurring Template Pattern`](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).
|
|
|
|
|
|
|
|
A derived trait class is expected to take a single template that corresponds to
|
|
|
|
the `ConcreteType`. An example trait definition is shown below:
|
|
|
|
|
|
|
|
```c++
|
|
|
|
template <typename ConcreteType>
|
2020-07-01 06:42:52 +08:00
|
|
|
class MyTrait : public TraitBase<ConcreteType, MyTrait> {
|
2019-11-21 10:19:38 +08:00
|
|
|
};
|
|
|
|
```
|
|
|
|
|
2022-02-26 02:17:30 +08:00
|
|
|
Operation traits may also provide a `verifyTrait` or `verifyRegionTrait` hook
|
|
|
|
that is called when verifying the concrete operation. The difference between
|
|
|
|
these two is that whether the verifier needs to access the regions, if so, the
|
|
|
|
operations in the regions will be verified before the verification of this
|
|
|
|
trait. The [verification order](OpDefinitions.md/#verification-ordering)
|
|
|
|
determines when a verifier will be invoked.
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
```c++
|
|
|
|
template <typename ConcreteType>
|
|
|
|
class MyTrait : public OpTrait::TraitBase<ConcreteType, MyTrait> {
|
|
|
|
public:
|
|
|
|
/// Override the 'verifyTrait' hook to add additional verification on the
|
|
|
|
/// concrete operation.
|
|
|
|
static LogicalResult verifyTrait(Operation *op) {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
Note: It is generally good practice to define the implementation of the
|
2022-02-26 02:17:30 +08:00
|
|
|
`verifyTrait` or `verifyRegionTrait` hook out-of-line as a free function when
|
|
|
|
possible to avoid instantiating the implementation for every concrete operation
|
|
|
|
type.
|
2019-11-21 10:19:38 +08:00
|
|
|
|
2021-10-13 07:14:57 +08:00
|
|
|
Operation traits may also provide a `foldTrait` hook that is called when folding
|
|
|
|
the concrete operation. The trait folders will only be invoked if the concrete
|
|
|
|
operation fold is either not implemented, fails, or performs an in-place fold.
|
2020-10-14 04:58:19 +08:00
|
|
|
|
2021-10-13 07:14:57 +08:00
|
|
|
The following signature of fold will be called if it is implemented and the op
|
|
|
|
has a single result.
|
2020-10-14 04:58:19 +08:00
|
|
|
|
|
|
|
```c++
|
|
|
|
template <typename ConcreteType>
|
|
|
|
class MyTrait : public OpTrait::TraitBase<ConcreteType, MyTrait> {
|
|
|
|
public:
|
|
|
|
/// Override the 'foldTrait' hook to support trait based folding on the
|
|
|
|
/// concrete operation.
|
|
|
|
static OpFoldResult foldTrait(Operation *op, ArrayRef<Attribute> operands) { {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
2021-10-13 07:14:57 +08:00
|
|
|
Otherwise, if the operation has a single result and the above signature is not
|
|
|
|
implemented, or the operation has multiple results, then the following signature
|
2020-10-14 04:58:19 +08:00
|
|
|
will be used (if implemented):
|
|
|
|
|
|
|
|
```c++
|
|
|
|
template <typename ConcreteType>
|
|
|
|
class MyTrait : public OpTrait::TraitBase<ConcreteType, MyTrait> {
|
|
|
|
public:
|
|
|
|
/// Override the 'foldTrait' hook to support trait based folding on the
|
|
|
|
/// concrete operation.
|
|
|
|
static LogicalResult foldTrait(Operation *op, ArrayRef<Attribute> operands,
|
|
|
|
SmallVectorImpl<OpFoldResult> &results) { {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
Note: It is generally good practice to define the implementation of the
|
|
|
|
`foldTrait` hook out-of-line as a free function when possible to avoid
|
|
|
|
instantiating the implementation for every concrete operation type.
|
|
|
|
|
2019-11-21 10:19:38 +08:00
|
|
|
### Parametric Traits
|
|
|
|
|
|
|
|
The above demonstrates the definition of a simple self-contained trait. It is
|
|
|
|
also often useful to provide some static parameters to the trait to control its
|
|
|
|
behavior. Given that the definition of the trait class is rigid, i.e. we must
|
2020-07-01 06:42:52 +08:00
|
|
|
have a single template argument for the concrete object, the templates for the
|
|
|
|
parameters will need to be split out. An example is shown below:
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
```c++
|
|
|
|
template <int Parameter>
|
|
|
|
class MyParametricTrait {
|
|
|
|
public:
|
|
|
|
template <typename ConcreteType>
|
2020-07-01 06:42:52 +08:00
|
|
|
class Impl : public TraitBase<ConcreteType, Impl> {
|
2019-11-21 10:19:38 +08:00
|
|
|
// Inside of 'Impl' we have full access to the template parameters
|
|
|
|
// specified above.
|
|
|
|
};
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
## Attaching a Trait
|
|
|
|
|
2020-07-01 06:42:52 +08:00
|
|
|
Traits may be used when defining a derived object type, by simply appending the
|
|
|
|
name of the trait class to the end of the base object class operation type:
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
```c++
|
2020-07-01 06:42:52 +08:00
|
|
|
/// Here we define 'MyAttr' along with the 'MyTrait' and `MyParametric trait
|
|
|
|
/// classes we defined previously.
|
|
|
|
class MyAttr : public Attribute::AttrBase<MyAttr, ..., MyTrait, MyParametricTrait<10>::Impl> {};
|
2019-12-10 01:23:15 +08:00
|
|
|
/// Here we define 'MyOp' along with the 'MyTrait' and `MyParametric trait
|
2019-11-21 10:19:38 +08:00
|
|
|
/// classes we defined previously.
|
|
|
|
class MyOp : public Op<MyOp, MyTrait, MyParametricTrait<10>::Impl> {};
|
2020-07-01 06:42:52 +08:00
|
|
|
/// Here we define 'MyType' along with the 'MyTrait' and `MyParametric trait
|
|
|
|
/// classes we defined previously.
|
|
|
|
class MyType : public Type::TypeBase<MyType, ..., MyTrait, MyParametricTrait<10>::Impl> {};
|
2019-11-21 10:19:38 +08:00
|
|
|
```
|
|
|
|
|
2020-07-01 06:42:52 +08:00
|
|
|
### Attaching Operation Traits in ODS
|
|
|
|
|
|
|
|
To use an operation trait in the [ODS](OpDefinitions.md) framework, we need to
|
|
|
|
provide a definition of the trait class. This can be done using the
|
|
|
|
`NativeOpTrait` and `ParamNativeOpTrait` classes. `ParamNativeOpTrait` provides
|
|
|
|
a mechanism in which to specify arguments to a parametric trait class with an
|
|
|
|
internal `Impl`.
|
2019-11-21 10:19:38 +08:00
|
|
|
|
2019-12-19 02:48:02 +08:00
|
|
|
```tablegen
|
2019-11-21 10:19:38 +08:00
|
|
|
// The argument is the c++ trait class name.
|
|
|
|
def MyTrait : NativeOpTrait<"MyTrait">;
|
|
|
|
|
|
|
|
// The first argument is the parent c++ class name. The second argument is a
|
|
|
|
// string containing the parameter list.
|
|
|
|
class MyParametricTrait<int prop>
|
|
|
|
: NativeOpTrait<"MyParametricTrait", !cast<string>(!head(parameters))>;
|
|
|
|
```
|
|
|
|
|
|
|
|
These can then be used in the `traits` list of an op definition:
|
|
|
|
|
2019-12-19 02:48:02 +08:00
|
|
|
```tablegen
|
2019-11-21 10:19:38 +08:00
|
|
|
def OpWithInferTypeInterfaceOp : Op<...[MyTrait, MyParametricTrait<10>]> { ... }
|
|
|
|
```
|
|
|
|
|
|
|
|
See the documentation on [operation definitions](OpDefinitions.md) for more
|
|
|
|
details.
|
|
|
|
|
|
|
|
## Using a Trait
|
|
|
|
|
|
|
|
Traits may be used to provide additional methods, static fields, or other
|
2020-07-01 06:42:52 +08:00
|
|
|
information directly on the concrete object. `Traits` internally become `Base`
|
|
|
|
classes of the concrete operation, so all of these are directly accessible. To
|
|
|
|
expose this information opaquely to transformations and analyses,
|
2019-11-21 10:19:38 +08:00
|
|
|
[`interfaces`](Interfaces.md) may be used.
|
|
|
|
|
2020-07-01 06:42:52 +08:00
|
|
|
To query if a specific object contains a specific trait, the `hasTrait<>` method
|
|
|
|
may be used. This takes as a template parameter the trait class, which is the
|
|
|
|
same as the one passed when attaching the trait to an operation.
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
```c++
|
|
|
|
Operation *op = ..;
|
|
|
|
if (op->hasTrait<MyTrait>() || op->hasTrait<MyParametricTrait<10>::Impl>())
|
|
|
|
...;
|
|
|
|
```
|
|
|
|
|
2020-07-01 06:42:52 +08:00
|
|
|
## Operation Traits List
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
MLIR provides a suite of traits that provide various functionalities that are
|
|
|
|
common across many different operations. Below is a list of some key traits that
|
|
|
|
may be used directly by any dialect. The format of the header for each trait
|
|
|
|
section goes as follows:
|
|
|
|
|
|
|
|
* `Header`
|
|
|
|
- (`C++ class` -- `ODS class`(if applicable))
|
|
|
|
|
2020-05-06 15:02:06 +08:00
|
|
|
### AffineScope
|
|
|
|
|
|
|
|
* `OpTrait::AffineScope` -- `AffineScope`
|
|
|
|
|
|
|
|
This trait is carried by region holding operations that define a new scope for
|
|
|
|
the purposes of polyhedral optimization and the affine dialect in particular.
|
|
|
|
Any SSA values of 'index' type that either dominate such operations, or are
|
|
|
|
defined at the top-level of such operations, or appear as region arguments for
|
|
|
|
such operations automatically become valid symbols for the polyhedral scope
|
|
|
|
defined by that operation. As a result, such SSA values could be used as the
|
|
|
|
operands or index operands of various affine dialect operations like affine.for,
|
2021-10-13 07:14:57 +08:00
|
|
|
affine.load, and affine.store. The polyhedral scope defined by an operation with
|
|
|
|
this trait includes all operations in its region excluding operations that are
|
|
|
|
nested inside of other operations that themselves have this trait.
|
2020-05-06 15:02:06 +08:00
|
|
|
|
2020-04-09 17:46:24 +08:00
|
|
|
### AutomaticAllocationScope
|
|
|
|
|
|
|
|
* `OpTrait::AutomaticAllocationScope` -- `AutomaticAllocationScope`
|
|
|
|
|
|
|
|
This trait is carried by region holding operations that define a new scope for
|
|
|
|
automatic allocation. Such allocations are automatically freed when control is
|
|
|
|
transferred back from the regions of such operations. As an example, allocations
|
2021-10-13 07:14:57 +08:00
|
|
|
performed by
|
|
|
|
[`memref.alloca`](Dialects/MemRef.md/#memrefalloca-mlirmemrefallocaop) are
|
[MLIR][NFC] add doc cross links from/to std.alloca
Add doc cross links between std.alloca and AutomaticAllocationScope.
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, grosul1, frgossen, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D77956
2020-04-12 05:11:45 +08:00
|
|
|
automatically freed when control leaves the region of its closest surrounding op
|
|
|
|
that has the trait AutomaticAllocationScope.
|
2020-04-09 17:46:24 +08:00
|
|
|
|
2019-11-21 10:19:38 +08:00
|
|
|
### Broadcastable
|
|
|
|
|
2020-01-12 01:42:18 +08:00
|
|
|
* `OpTrait::ResultsBroadcastableShape` -- `ResultsBroadcastableShape`
|
2019-11-21 10:19:38 +08:00
|
|
|
|
2020-01-12 01:42:18 +08:00
|
|
|
This trait adds the property that the operation is known to have
|
2019-11-21 10:19:38 +08:00
|
|
|
[broadcast-compatible](https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html)
|
2020-01-12 01:42:18 +08:00
|
|
|
operands and its result types' shape is the broadcast compatible with the shape
|
|
|
|
of the broadcasted operands. Specifically, starting from the most varying
|
|
|
|
dimension, each dimension pair of the two operands' shapes should either be the
|
|
|
|
same or one of them is one. Also, the result shape should have the corresponding
|
2019-11-21 10:19:38 +08:00
|
|
|
dimension equal to the larger one, if known. Shapes are checked partially if
|
|
|
|
ranks or dimensions are not known. For example, an op with `tensor<?x2xf32>` and
|
|
|
|
`tensor<2xf32>` as operand types and `tensor<3x2xf32>` as the result type is
|
|
|
|
broadcast-compatible.
|
|
|
|
|
2020-01-12 01:42:18 +08:00
|
|
|
This trait requires that the operands are either vector or tensor types.
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
### Commutative
|
|
|
|
|
|
|
|
* `OpTrait::IsCommutative` -- `Commutative`
|
|
|
|
|
|
|
|
This trait adds the property that the operation is commutative, i.e. `X op Y ==
|
|
|
|
Y op X`
|
|
|
|
|
2020-11-04 10:17:55 +08:00
|
|
|
### ElementwiseMappable
|
|
|
|
|
2021-10-13 07:14:57 +08:00
|
|
|
* `OpTrait::ElementwiseMappable` -- `ElementwiseMappable`
|
2020-11-04 10:17:55 +08:00
|
|
|
|
|
|
|
This trait tags scalar ops that also can be applied to vectors/tensors, with
|
|
|
|
their semantics on vectors/tensors being elementwise application. This trait
|
|
|
|
establishes a set of properties that allow reasoning about / converting between
|
|
|
|
scalar/vector/tensor code. These same properties allow blanket implementations
|
|
|
|
of various analyses/transformations for all `ElementwiseMappable` ops.
|
|
|
|
|
|
|
|
Note: Not all ops that are "elementwise" in some abstract sense satisfy this
|
|
|
|
trait. In particular, broadcasting behavior is not allowed. See the comments on
|
|
|
|
`OpTrait::ElementwiseMappable` for the precise requirements.
|
|
|
|
|
2019-11-21 10:19:38 +08:00
|
|
|
### HasParent
|
|
|
|
|
2020-10-27 06:45:57 +08:00
|
|
|
* `OpTrait::HasParent<typename ParentOpType>` -- `HasParent<string op>` or
|
|
|
|
`ParentOneOf<list<string> opList>`
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
This trait provides APIs and verifiers for operations that can only be nested
|
|
|
|
within regions that are attached to operations of `ParentOpType`.
|
|
|
|
|
|
|
|
### IsolatedFromAbove
|
|
|
|
|
|
|
|
* `OpTrait::IsIsolatedFromAbove` -- `IsolatedFromAbove`
|
|
|
|
|
|
|
|
This trait signals that the regions of an operations are known to be isolated
|
|
|
|
from above. This trait asserts that the regions of an operation will not
|
|
|
|
capture, or reference, SSA values defined above the region scope. This means
|
|
|
|
that the following is invalid if `foo.region_op` is defined as
|
|
|
|
`IsolatedFromAbove`:
|
|
|
|
|
|
|
|
```mlir
|
2021-10-13 07:14:57 +08:00
|
|
|
%result = arith.constant 10 : i32
|
2019-11-21 10:19:38 +08:00
|
|
|
foo.region_op {
|
|
|
|
foo.yield %result : i32
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
This trait is an important structural property of the IR, and enables operations
|
2020-04-19 12:37:26 +08:00
|
|
|
to have [passes](PassManagement.md) scheduled under them.
|
2019-11-21 10:19:38 +08:00
|
|
|
|
2020-08-27 13:17:33 +08:00
|
|
|
### MemRefsNormalizable
|
|
|
|
|
2021-10-13 07:14:57 +08:00
|
|
|
* `OpTrait::MemRefsNormalizable` -- `MemRefsNormalizable`
|
2020-08-27 13:17:33 +08:00
|
|
|
|
2021-10-13 07:14:57 +08:00
|
|
|
This trait is used to flag operations that consume or produce values of `MemRef`
|
|
|
|
type where those references can be 'normalized'. In cases where an associated
|
|
|
|
`MemRef` has a non-identity memory-layout specification, such normalizable
|
|
|
|
operations can be modified so that the `MemRef` has an identity layout
|
|
|
|
specification. This can be implemented by associating the operation with its own
|
2020-09-30 08:14:42 +08:00
|
|
|
index expression that can express the equivalent of the memory-layout
|
|
|
|
specification of the MemRef type. See [the -normalize-memrefs pass].
|
|
|
|
(https://mlir.llvm.org/docs/Passes/#-normalize-memrefs-normalize-memrefs)
|
2020-08-27 13:17:33 +08:00
|
|
|
|
2021-03-12 07:58:02 +08:00
|
|
|
### Single Block Region
|
|
|
|
|
|
|
|
* `OpTrait::SingleBlock` -- `SingleBlock`
|
|
|
|
|
|
|
|
This trait provides APIs and verifiers for operations with regions that have a
|
|
|
|
single block.
|
|
|
|
|
2019-11-21 10:19:38 +08:00
|
|
|
### Single Block with Implicit Terminator
|
|
|
|
|
2021-03-12 07:58:02 +08:00
|
|
|
* `OpTrait::SingleBlockImplicitTerminator<typename TerminatorOpType>` --
|
2019-11-21 10:19:38 +08:00
|
|
|
`SingleBlockImplicitTerminator<string op>`
|
|
|
|
|
2021-03-12 07:58:02 +08:00
|
|
|
This trait implies the `SingleBlock` above, but adds the additional requirement
|
|
|
|
that the single block must terminate with `TerminatorOpType`.
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
### SymbolTable
|
|
|
|
|
|
|
|
* `OpTrait::SymbolTable` -- `SymbolTable`
|
|
|
|
|
2020-02-09 02:40:00 +08:00
|
|
|
This trait is used for operations that define a
|
|
|
|
[`SymbolTable`](SymbolsAndSymbolTables.md#symbol-table).
|
2019-11-21 10:19:38 +08:00
|
|
|
|
|
|
|
### Terminator
|
|
|
|
|
|
|
|
* `OpTrait::IsTerminator` -- `Terminator`
|
|
|
|
|
|
|
|
This trait provides verification and functionality for operations that are known
|
|
|
|
to be [terminators](LangRef.md#terminator-operations).
|
2021-03-12 07:58:02 +08:00
|
|
|
|
|
|
|
* `OpTrait::NoTerminator` -- `NoTerminator`
|
|
|
|
|
|
|
|
This trait removes the requirement on regions held by an operation to have
|
|
|
|
[terminator operations](LangRef.md#terminator-operations) at the end of a block.
|
|
|
|
This requires that these regions have a single block. An example of operation
|
|
|
|
using this trait is the top-level `ModuleOp`.
|