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