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