1# Interfaces
2
3MLIR is generic and very extensible; it allows for opaquely representing many
4different dialects that have their own operations, attributes, types, and so on.
5This allows for dialects to be very expressive in their semantics and for MLIR
6to capture many different levels of abstraction. The downside to this is that
7transformations and analyses must be extremely conservative about the operations
8that they encounter, and must special-case the different dialects that they
9support. To combat this, MLIR provides the concept of `interfaces`.
10
11## Motivation
12
13Interfaces provide a generic way of interacting with the IR. The goal is to be
14able to express transformations/analyses in terms of these interfaces without
15encoding specific knowledge about the exact operation or dialect involved. This
16makes the compiler more extensible by allowing the addition of new dialects and
17operations in a decoupled way with respect to the implementation of
18transformations/analyses.
19
20### Dialect Interfaces
21
22Dialect interfaces are generally useful for transformation passes or analyses
23that want to opaquely operate on operations, even *across* dialects. These
24interfaces generally involve wide coverage over the entire dialect and are only
25used for a handful of transformations/analyses. In these cases, registering the
26interface directly on each operation is overly complex and cumbersome. The
27interface is not core to the operation, just to the specific transformation. An
28example of where this type of interface would be used is inlining. Inlining
29generally queries high-level information about the operations within a dialect,
30like legality and cost modeling, that often is not specific to one operation.
31
32A dialect interface can be defined by inheriting from the CRTP base class
33`DialectInterfaceBase::Base`. This class provides the necessary utilities for
34registering an interface with the dialect so that it can be looked up later.
35Once the interface has been defined, dialects can override it using
36dialect-specific information. The interfaces defined by a dialect are registered
37in a similar mechanism to Attributes, Operations, Types, etc.
38
39```c++
40/// Define an Inlining interface to allow for dialects to opt-in.
41class DialectInlinerInterface :
42    public DialectInterface::Base<DialectInlinerInterface> {
43public:
44  /// Returns true if the given region 'src' can be inlined into the region
45  /// 'dest' that is attached to an operation registered to the current dialect.
46  /// 'valueMapping' contains any remapped values from within the 'src' region.
47  /// This can be used to examine what values will replace entry arguments into
48  /// the 'src' region, for example.
49  virtual bool isLegalToInline(Region *dest, Region *src,
50                               BlockAndValueMapping &valueMapping) const {
51    return false;
52  }
53};
54
55/// Override the inliner interface to add support for inlining affine
56/// operations.
57struct AffineInlinerInterface : public DialectInlinerInterface {
58  /// Affine structures have specific inlining constraints.
59  bool isLegalToInline(Region *dest, Region *src,
60                       BlockAndValueMapping &valueMapping) const final {
61    ...
62  }
63};
64
65/// Register the interface with the dialect.
66AffineDialect::AffineDialect(MLIRContext *context) ... {
67  addInterfaces<AffineInlinerInterface>();
68}
69```
70
71Once registered, these interfaces can be opaquely queried from the dialect by
72the transformation/analysis that wants to use them:
73
74```c++
75Dialect *dialect = ...;
76if (auto *interface = dialect->getInterface<DialectInlinerInterface>())
77    ... // The dialect provides this interface.
78```
79
80#### DialectInterfaceCollections
81
82An additional utility is provided via DialectInterfaceCollection. This CRTP
83class allows for collecting all of the dialects that have registered a given
84interface within the context.
85
86```c++
87class InlinerInterface : public
88    DialectInterfaceCollection<DialectInlinerInterface> {
89  /// The hooks for this class mirror the hooks for the DialectInlinerInterface,
90  /// with default implementations that call the hook on the interface for a
91  /// given dialect.
92  virtual bool isLegalToInline(Region *dest, Region *src,
93                               BlockAndValueMapping &valueMapping) const {
94    auto *handler = getInterfaceFor(dest->getContainingOp());
95    return handler ? handler->isLegalToInline(dest, src, valueMapping) : false;
96  }
97};
98
99MLIRContext *ctx = ...;
100InlinerInterface interface(ctx);
101if(!interface.isLegalToInline(...))
102   ...
103```
104
105### Operation Interfaces
106
107Operation interfaces, as the name suggests, are those registered at the
108Operation level. These interfaces provide an opaque view into derived operations
109by providing a virtual interface that must be implemented. As an example, the
110`Linalg` dialect may implement an interface that provides general queries about
111some of the dialects library operations. These queries may provide things like:
112the number of parallel loops; the number of inputs and outputs; etc.
113
114Operation interfaces are defined by overriding the CRTP base class
115`OpInterface`. This class takes, as a template parameter, a `Traits` class that
116defines a `Concept` and a `Model` class. These classes provide an implementation
117of concept-based polymorphism, where the Concept defines a set of virtual
118methods that are overridden by the Model that is templated on the concrete
119operation type. It is important to note that these classes should be pure in
120that they contain no non-static data members. Operations that wish to override
121this interface should add the provided trait `OpInterface<..>::Trait` upon
122registration.
123
124```c++
125struct ExampleOpInterfaceTraits {
126  /// Define a base concept class that defines the virtual interface that needs
127  /// to be overridden.
128  struct Concept {
129    virtual ~Concept();
130    virtual unsigned getNumInputs(Operation *op) = 0;
131  };
132
133  /// Define a model class that specializes a concept on a given operation type.
134  template <typename OpT>
135  struct Model : public Concept {
136    /// Override the method to dispatch on the concrete operation.
137    unsigned getNumInputs(Operation *op) final {
138      return llvm::cast<OpT>(op).getNumInputs();
139    }
140  };
141};
142
143class ExampleOpInterface : public OpInterface<ExampleOpInterface,
144                                              ExampleOpInterfaceTraits> {
145public:
146  /// Use base class constructor to support LLVM-style casts.
147  using OpInterface<ExampleOpInterface, ExampleOpInterfaceTraits>::OpInterface;
148
149  /// The interface dispatches to 'getImpl()', an instance of the concept.
150  unsigned getNumInputs() {
151    return getImpl()->getNumInputs(getOperation());
152  }
153};
154
155```
156
157Once the interface has been defined, it is registered to an operation by adding
158the provided trait `ExampleOpInterface::Trait`. Using this interface is just
159like using any other derived operation type, i.e. casting:
160
161```c++
162/// When defining the operation, the interface is registered via the nested
163/// 'Trait' class provided by the 'OpInterface<>' base class.
164class MyOp : public Op<MyOp, ExampleOpInterface::Trait> {
165public:
166  /// The definition of the interface method on the derived operation.
167  unsigned getNumInputs() { return ...; }
168};
169
170/// Later, we can query if a specific operation(like 'MyOp') overrides the given
171/// interface.
172Operation *op = ...;
173if (ExampleOpInterface example = dyn_cast<ExampleOpInterface>(op))
174  llvm::errs() << "num inputs = " << example.getNumInputs() << "\n";
175```
176
177#### Utilizing the ODS Framework
178
179Operation interfaces require a bit of boiler plate to connect all of the pieces
180together. The ODS(Operation Definition Specification) framework provides
181simplified mechanisms for
182[defining interfaces](OpDefinitions.md#operation-interfaces).
183
184As an example, using the ODS framework would allow for defining the example
185interface above as:
186
187```tablegen
188def ExampleOpInterface : OpInterface<"ExampleOpInterface"> {
189  let description = [{
190    This is an example interface definition.
191  }];
192
193  let methods = [
194    InterfaceMethod<
195      "Get the number of inputs for the current operation.",
196      "unsigned", "getNumInputs"
197    >,
198  ];
199}
200```
201