• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Interfaces
2
3MLIR is a generic and extensible framework, representing different
4dialects with their own operations, attributes, types, and so on.
5MLIR Dialects can express operations with a wide variety of semantics
6and different levels of abstraction. The downside to this is that MLIR
7transformations and analyses need to account for the semantics of
8every operation, or handle operations conservatively. Without care,
9this can result in code with special-cases for each supported
10operation type. To combat this, MLIR provides the concept of
11`interfaces`.
12
13## Motivation
14
15Interfaces provide a generic way of interacting with the IR. The goal is to be
16able to express transformations/analyses in terms of these interfaces without
17encoding specific knowledge about the exact operation or dialect involved. This
18makes the compiler more extensible by allowing the addition of new dialects and
19operations in a decoupled way with respect to the implementation of
20transformations/analyses.
21
22### Dialect Interfaces
23
24Dialect interfaces are generally useful for transformation passes or analyses
25that want to operate generically on a set of attributes/operations/types, which
26might even be defined in different dialects. These interfaces generally involve
27wide coverage over the entire dialect and are only used for a handful of
28transformations/analyses. In these cases, registering the interface directly on
29each operation is overly complex and cumbersome. The interface is not core to
30the operation, just to the specific transformation. An example of where this
31type of interface would be used is inlining. Inlining generally queries
32high-level information about the operations within a dialect, like legality and
33cost modeling, that often is not specific to one operation.
34
35A dialect interface can be defined by inheriting from the CRTP base class
36`DialectInterfaceBase::Base`. This class provides the necessary utilities for
37registering an interface with the dialect so that it can be looked up later.
38Once the interface has been defined, dialects can override it using
39dialect-specific information. The interfaces defined by a dialect are registered
40in a similar mechanism to Attributes, Operations, Types, etc.
41
42```c++
43/// Define an Inlining interface to allow for dialects to opt-in.
44class DialectInlinerInterface :
45    public DialectInterface::Base<DialectInlinerInterface> {
46public:
47  /// Returns true if the given region 'src' can be inlined into the region
48  /// 'dest' that is attached to an operation registered to the current dialect.
49  /// 'valueMapping' contains any remapped values from within the 'src' region.
50  /// This can be used to examine what values will replace entry arguments into
51  /// the 'src' region, for example.
52  virtual bool isLegalToInline(Region *dest, Region *src,
53                               BlockAndValueMapping &valueMapping) const {
54    return false;
55  }
56};
57
58/// Override the inliner interface to add support for inlining affine
59/// operations.
60struct AffineInlinerInterface : public DialectInlinerInterface {
61  /// Affine structures have specific inlining constraints.
62  bool isLegalToInline(Region *dest, Region *src,
63                       BlockAndValueMapping &valueMapping) const final {
64    ...
65  }
66};
67
68/// Register the interface with the dialect.
69AffineDialect::AffineDialect(MLIRContext *context) ... {
70  addInterfaces<AffineInlinerInterface>();
71}
72```
73
74Once registered, these interfaces can be queried from the dialect by
75the transformation/analysis that wants to use them, without
76determining the particular dialect subclass:
77
78```c++
79Dialect *dialect = ...;
80if (auto *interface = dialect->getInterface<DialectInlinerInterface>())
81    ... // The dialect provides this interface.
82```
83
84#### DialectInterfaceCollections
85
86An additional utility is provided via DialectInterfaceCollection. This CRTP
87class allows for collecting all of the dialects that have registered a given
88interface within the context.
89
90```c++
91class InlinerInterface : public
92    DialectInterfaceCollection<DialectInlinerInterface> {
93  /// The hooks for this class mirror the hooks for the DialectInlinerInterface,
94  /// with default implementations that call the hook on the interface for a
95  /// given dialect.
96  virtual bool isLegalToInline(Region *dest, Region *src,
97                               BlockAndValueMapping &valueMapping) const {
98    auto *handler = getInterfaceFor(dest->getContainingOp());
99    return handler ? handler->isLegalToInline(dest, src, valueMapping) : false;
100  }
101};
102
103MLIRContext *ctx = ...;
104InlinerInterface interface(ctx);
105if(!interface.isLegalToInline(...))
106   ...
107```
108
109### Attribute/Operation/Type Interfaces
110
111Attribute/Operation/Type interfaces, as the names suggest, are those registered
112at the level of a specific attribute/operation/type. These interfaces provide
113access to derived objects by providing a virtual interface that must be
114implemented. As an example, the `Linalg` dialect may implement an interface that
115provides general queries about some of the dialects library operations. These
116queries may provide things like: the number of parallel loops; the number of
117inputs and outputs; etc.
118
119These interfaces are defined by overriding the CRTP base class `AttrInterface`,
120`OpInterface`, or `TypeInterface` respectively. These classes take, as a
121template parameter, a `Traits` class that defines a `Concept` and a `Model`
122class. These classes provide an implementation of concept-based polymorphism,
123where the Concept defines a set of virtual methods that are overridden by the
124Model that is templated on the concrete object type. It is important to note
125that these classes should be pure in that they contain no non-static data
126members. Objects that wish to override this interface should add the provided
127trait `*Interface<..>::Trait` to the trait list upon registration.
128
129```c++
130struct ExampleOpInterfaceTraits {
131  /// Define a base concept class that defines the virtual interface that needs
132  /// to be overridden.
133  struct Concept {
134    virtual ~Concept();
135    virtual unsigned getNumInputs(Operation *op) const = 0;
136  };
137
138  /// Define a model class that specializes a concept on a given operation type.
139  template <typename OpT>
140  struct Model : public Concept {
141    /// Override the method to dispatch on the concrete operation.
142    unsigned getNumInputs(Operation *op) const final {
143      return llvm::cast<OpT>(op).getNumInputs();
144    }
145  };
146};
147
148class ExampleOpInterface : public OpInterface<ExampleOpInterface,
149                                              ExampleOpInterfaceTraits> {
150public:
151  /// Use base class constructor to support LLVM-style casts.
152  using OpInterface<ExampleOpInterface, ExampleOpInterfaceTraits>::OpInterface;
153
154  /// The interface dispatches to 'getImpl()', an instance of the concept.
155  unsigned getNumInputs() const {
156    return getImpl()->getNumInputs(getOperation());
157  }
158};
159
160```
161
162Once the interface has been defined, it is registered to an operation by adding
163the provided trait `ExampleOpInterface::Trait`. Using this interface is just
164like using any other derived operation type, i.e. casting:
165
166```c++
167/// When defining the operation, the interface is registered via the nested
168/// 'Trait' class provided by the 'OpInterface<>' base class.
169class MyOp : public Op<MyOp, ExampleOpInterface::Trait> {
170public:
171  /// The definition of the interface method on the derived operation.
172  unsigned getNumInputs() { return ...; }
173};
174
175/// Later, we can query if a specific operation(like 'MyOp') overrides the given
176/// interface.
177Operation *op = ...;
178if (ExampleOpInterface example = dyn_cast<ExampleOpInterface>(op))
179  llvm::errs() << "num inputs = " << example.getNumInputs() << "\n";
180```
181
182#### Utilizing the ODS Framework
183
184Operation interfaces require a bit of boiler plate to connect all of the pieces
185together. The ODS(Operation Definition Specification) framework provides
186simplified mechanisms for [defining interfaces](OpDefinitions.md#interfaces).
187
188As an example, using the ODS framework would allow for defining the example
189interface above as:
190
191```tablegen
192def ExampleOpInterface : OpInterface<"ExampleOpInterface"> {
193  let description = [{
194    This is an example interface definition.
195  }];
196
197  let methods = [
198    InterfaceMethod<
199      "Get the number of inputs for the current operation.",
200      "unsigned", "getNumInputs"
201    >,
202  ];
203}
204```
205
206#### Operation Interface List
207
208MLIR includes standard interfaces providing functionality that is
209likely to be common across many different operations. Below is a list
210of some key interfaces that may be used directly by any dialect. The
211format of the header for each interface section goes as follows:
212
213*   `Interface class name`
214    -   (`C++ class` -- `ODS class`(if applicable))
215
216##### CallInterfaces
217
218*   `CallOpInterface` - Used to represent operations like 'call'
219    -   `CallInterfaceCallable getCallableForCallee()`
220*   `CallableOpInterface` - Used to represent the target callee of call.
221    -   `Region * getCallableRegion()`
222    -   `ArrayRef<Type> getCallableResults()`
223
224##### RegionKindInterfaces
225
226*   `RegionKindInterface` - Used to describe the abstract semantics of regions.
227     - `RegionKind getRegionKind(unsigned index)` - Return the kind of the region with the given index inside this operation.
228         - RegionKind::Graph - represents a graph region without control flow semantics
229         - RegionKind::SSACFG - represents an [SSA-style control flow](LangRef.md#modeling-control-flow) region with basic blocks and reachability
230     - `hasSSADominance(unsigned index)` - Return true if the region with the given index inside this operation requires dominance.
231
232##### SymbolInterfaces
233
234*   `SymbolOpInterface` - Used to represent
235    [`Symbol`](SymbolsAndSymbolTables.md#symbol) operations which reside
236    immediately within a region that defines a
237    [`SymbolTable`](SymbolsAndSymbolTables.md#symbol-table).
238
239*   `SymbolUserOpInterface` - Used to represent operations that reference
240    [`Symbol`](SymbolsAndSymbolTables.md#symbol) operations. This provides the
241    ability to perform safe and efficient verification of symbol uses, as well
242    as additional functionality.
243