[llvm-branch-commits] [mlir] d1e0545 - [mlir][Interfaces] Tidy up the documentation for interfaces

River Riddle via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Dec 9 15:38:34 PST 2020


Author: River Riddle
Date: 2020-12-09T15:34:07-08:00
New Revision: d1e0545445ced928ae4f0437997278e09cefde1d

URL: https://github.com/llvm/llvm-project/commit/d1e0545445ced928ae4f0437997278e09cefde1d
DIFF: https://github.com/llvm/llvm-project/commit/d1e0545445ced928ae4f0437997278e09cefde1d.diff

LOG: [mlir][Interfaces] Tidy up the documentation for interfaces

The documentation has become a bit stale with age, and doesn't include great documentation for some newer concepts. This revision tidies up a majority of it, with some more cleanup to come in the future. The documentation for the declarative specification is also moved from OpDefinitions.md to Interfaces.md, which is a much more logical place for it to live.

Differential Revision: https://reviews.llvm.org/D92895

Added: 
    

Modified: 
    mlir/docs/Interfaces.md
    mlir/docs/OpDefinitions.md

Removed: 
    


################################################################################
diff  --git a/mlir/docs/Interfaces.md b/mlir/docs/Interfaces.md
index 9e32a70888aa..4fe8421295ec 100644
--- a/mlir/docs/Interfaces.md
+++ b/mlir/docs/Interfaces.md
@@ -1,46 +1,47 @@
 # Interfaces
 
-MLIR is a generic and extensible framework, representing 
diff erent
-dialects with their own operations, attributes, types, and so on.
-MLIR Dialects can express operations with a wide variety of semantics
-and 
diff erent levels of abstraction. The downside to this is that MLIR
-transformations and analyses need to account for the semantics of
-every operation, or handle operations conservatively. Without care,
-this can result in code with special-cases for each supported
-operation type. To combat this, MLIR provides the concept of
-`interfaces`.
+MLIR is a generic and extensible framework, representing 
diff erent dialects with
+their own attributes, operations, types, and so on. MLIR Dialects can express
+operations with a wide variety of semantics and 
diff erent levels of abstraction.
+The downside to this is that MLIR transformations and analyses need to be able
+to account for the semantics of every operation, or be overly conservative.
+Without care, this can result in code with special-cases for each supported
+operation type. To combat this, MLIR provides a concept of `interfaces`.
 
 ## Motivation
 
 Interfaces provide a generic way of interacting with the IR. The goal is to be
 able to express transformations/analyses in terms of these interfaces without
 encoding specific knowledge about the exact operation or dialect involved. This
-makes the compiler more extensible by allowing the addition of new dialects and
-operations in a decoupled way with respect to the implementation of
+makes the compiler more easily extensible by allowing the addition of new
+dialects and operations in a decoupled way with respect to the implementation of
 transformations/analyses.
 
 ### Dialect Interfaces
 
 Dialect interfaces are generally useful for transformation passes or analyses
 that want to operate generically on a set of attributes/operations/types, which
-might even be defined in 
diff erent dialects. These interfaces generally involve
-wide coverage over the entire dialect and are only used for a handful of
-transformations/analyses. In these cases, registering the interface directly on
-each operation is overly complex and cumbersome. The interface is not core to
-the operation, just to the specific transformation. An example of where this
-type of interface would be used is inlining. Inlining generally queries
-high-level information about the operations within a dialect, like legality and
-cost modeling, that often is not specific to one operation.
-
-A dialect interface can be defined by inheriting from the CRTP base class
-`DialectInterfaceBase::Base`. This class provides the necessary utilities for
-registering an interface with the dialect so that it can be looked up later.
-Once the interface has been defined, dialects can override it using
-dialect-specific information. The interfaces defined by a dialect are registered
-in a similar mechanism to Attributes, Operations, Types, etc.
+may be defined in 
diff erent dialects. These interfaces generally involve wide
+coverage over an entire dialect and are only used for a handful of analyses or
+transformations. In these cases, registering the interface directly on each
+operation is overly complex and cumbersome. The interface is not core to the
+operation, just to the specific transformation. An example of where this type of
+interface would be used is inlining. Inlining generally queries high-level
+information about the operations within a dialect, like cost modeling and
+legality, that often is not specific to one operation.
+
+A dialect interface can be defined by inheriting from the
+[CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) base
+class `DialectInterfaceBase::Base<>`. This class provides the necessary
+utilities for registering an interface with a dialect so that it can be
+referenced later. Once the interface has been defined, dialects can override it
+using dialect-specific information. The interfaces defined by a dialect are
+registered via `addInterfaces<>`, a similar mechanism to Attributes, Operations,
+Types, etc
 
 ```c++
-/// Define an Inlining interface to allow for dialects to opt-in.
+/// Define a base inlining interface class to allow for dialects to opt-in to
+/// the inliner.
 class DialectInlinerInterface :
     public DialectInterface::Base<DialectInlinerInterface> {
 public:
@@ -55,8 +56,8 @@ public:
   }
 };
 
-/// Override the inliner interface to add support for inlining affine
-/// operations.
+/// Override the inliner interface to add support for the AffineDialect to
+/// enable inlining affine operations.
 struct AffineInlinerInterface : public DialectInlinerInterface {
   /// Affine structures have specific inlining constraints.
   bool isLegalToInline(Region *dest, Region *src,
@@ -71,21 +72,24 @@ AffineDialect::AffineDialect(MLIRContext *context) ... {
 }
 ```
 
-Once registered, these interfaces can be queried from the dialect by
-the transformation/analysis that wants to use them, without
-determining the particular dialect subclass:
+Once registered, these interfaces can be queried from the dialect by an analysis
+or transformation without the need to determine the specific dialect subclass:
 
 ```c++
 Dialect *dialect = ...;
-if (auto *interface = dialect->getInterface<DialectInlinerInterface>())
-    ... // The dialect provides this interface.
+if (DialectInlinerInterface *interface
+      = dialect->getRegisteredInterface<DialectInlinerInterface>()) {
+  // The dialect has provided an implementation of this interface.
+  ...
+}
 ```
 
-#### DialectInterfaceCollections
+#### DialectInterfaceCollection
 
-An additional utility is provided via DialectInterfaceCollection. This CRTP
-class allows for collecting all of the dialects that have registered a given
-interface within the context.
+An additional utility is provided via `DialectInterfaceCollection`. This class
+allows for collecting all of the dialects that have registered a given interface
+within an instance of the `MLIRContext`. This can be useful to hide and optimize
+the lookup of a registered dialect interface.
 
 ```c++
 class InlinerInterface : public
@@ -111,57 +115,80 @@ if(!interface.isLegalToInline(...))
 Attribute/Operation/Type interfaces, as the names suggest, are those registered
 at the level of a specific attribute/operation/type. These interfaces provide
 access to derived objects by providing a virtual interface that must be
-implemented. As an example, the `Linalg` dialect may implement an interface that
-provides general queries about some of the dialects library operations. These
-queries may provide things like: the number of parallel loops; the number of
-inputs and outputs; etc.
-
-These interfaces are defined by overriding the CRTP base class `AttrInterface`,
-`OpInterface`, or `TypeInterface` respectively. These classes take, as a
-template parameter, a `Traits` class that defines a `Concept` and a `Model`
-class. These classes provide an implementation of concept-based polymorphism,
-where the Concept defines a set of virtual methods that are overridden by the
-Model that is templated on the concrete object type. It is important to note
-that these classes should be pure in that they contain no non-static data
-members. Objects that wish to override this interface should add the provided
-trait `*Interface<..>::Trait` to the trait list upon registration.
+implemented. As an example, many analyses and transformations want to reason
+about the side effects of an operation to improve performance and correctness.
+The side effects of an operation are generally tied to the semantics of a
+specific operation, for example an `affine.load` operation has a `write` effect
+(as the name may suggest).
+
+These interfaces are defined by overriding the
+[CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) class
+for the specific IR entity; `AttrInterface`, `OpInterface`, or `TypeInterface`
+respectively. These classes take, as a template parameter, a `Traits` class that
+defines a `Concept` and a `Model` class. These classes provide an implementation
+of concept-based polymorphism, where the `Concept` defines a set of virtual
+methods that are overridden by the `Model` that is templated on the concrete
+entity type. It is important to note that these classes should be pure, and
+should not contain non-static data members or other mutable data. To attach an
+interface to an object, the base interface classes provide a
+[`Trait`](Traits.md) class that can be appended to the trait list of that
+object.
 
 ```c++
 struct ExampleOpInterfaceTraits {
-  /// Define a base concept class that defines the virtual interface that needs
-  /// to be overridden.
+  /// Define a base concept class that specifies the virtual interface to be
+  /// implemented.
   struct Concept {
     virtual ~Concept();
-    virtual unsigned getNumInputs(Operation *op) const = 0;
+
+    /// This is an example of a non-static hook to an operation.
+    virtual unsigned exampleInterfaceHook(Operation *op) const = 0;
+
+    /// This is an example of a static hook to an operation. A static hook does
+    /// not require a concrete instance of the operation. The implementation is
+    /// a virtual hook, the same as the non-static case, because the
+    /// implementation of the hook itself still requires indirection.
+    virtual unsigned exampleStaticInterfaceHook() const = 0;
   };
 
   /// Define a model class that specializes a concept on a given operation type.
-  template <typename OpT>
+  template <typename ConcreteOp>
   struct Model : public Concept {
     /// Override the method to dispatch on the concrete operation.
-    unsigned getNumInputs(Operation *op) const final {
-      return llvm::cast<OpT>(op).getNumInputs();
+    unsigned exampleInterfaceHook(Operation *op) const final {
+      return llvm::cast<ConcreteOp>(op).exampleInterfaceHook();
+    }
+
+    /// Override the static method to dispatch to the concrete operation type.
+    unsigned exampleStaticInterfaceHook() const final {
+      return ConcreteOp::exampleStaticInterfaceHook();
     }
   };
 };
 
+/// Define the main interface class that analyses and transformations will
+/// interface with.
 class ExampleOpInterface : public OpInterface<ExampleOpInterface,
                                               ExampleOpInterfaceTraits> {
 public:
-  /// Use base class constructor to support LLVM-style casts.
+  /// Inherit the base class constructor to support LLVM-style casting.
   using OpInterface<ExampleOpInterface, ExampleOpInterfaceTraits>::OpInterface;
 
-  /// The interface dispatches to 'getImpl()', an instance of the concept.
-  unsigned getNumInputs() const {
-    return getImpl()->getNumInputs(getOperation());
+  /// The interface dispatches to 'getImpl()', a method provided by the base
+  /// `OpInterface` class that returns an instance of the concept.
+  unsigned exampleInterfaceHook() const {
+    return getImpl()->exampleInterfaceHook(getOperation());
+  }
+  unsigned exampleStaticInterfaceHook() const {
+    return getImpl()->exampleStaticInterfaceHook(getOperation()->getName());
   }
 };
 
 ```
 
 Once the interface has been defined, it is registered to an operation by adding
-the provided trait `ExampleOpInterface::Trait`. Using this interface is just
-like using any other derived operation type, i.e. casting:
+the provided trait `ExampleOpInterface::Trait` as described earlier. Using this
+interface is just like using any other derived operation type, i.e. casting:
 
 ```c++
 /// When defining the operation, the interface is registered via the nested
@@ -169,21 +196,29 @@ like using any other derived operation type, i.e. casting:
 class MyOp : public Op<MyOp, ExampleOpInterface::Trait> {
 public:
   /// The definition of the interface method on the derived operation.
-  unsigned getNumInputs() { return ...; }
+  unsigned exampleInterfaceHook() { return ...; }
+  static unsigned exampleStaticInterfaceHook() { return ...; }
 };
 
 /// Later, we can query if a specific operation(like 'MyOp') overrides the given
 /// interface.
 Operation *op = ...;
 if (ExampleOpInterface example = dyn_cast<ExampleOpInterface>(op))
-  llvm::errs() << "num inputs = " << example.getNumInputs() << "\n";
+  llvm::errs() << "hook returned = " << example.exampleInterfaceHook() << "\n";
 ```
 
 #### Utilizing the ODS Framework
 
-Operation interfaces require a bit of boiler plate to connect all of the pieces
-together. The ODS(Operation Definition Specification) framework provides
-simplified mechanisms for [defining interfaces](OpDefinitions.md#interfaces).
+Note: Before reading this section, the reader should have some familiarity with
+the concepts described in the
+[`Operation Definition Specification`](OpDefinitions.md) documentation.
+
+As detailed above, [Interfaces](attribute-operation-type-interfaces) allow for
+attributes, operations, and types to expose method calls without requiring that
+the caller know the specific derived type. The downside to this infrastructure,
+is that it requires a bit of boiler plate to connect all of the pieces together.
+MLIR provides a mechanism with which to defines interfaces declaratively in ODS,
+and have the C++ definitions auto-generated.
 
 As an example, using the ODS framework would allow for defining the example
 interface above as:
@@ -196,19 +231,270 @@ def ExampleOpInterface : OpInterface<"ExampleOpInterface"> {
 
   let methods = [
     InterfaceMethod<
-      "Get the number of inputs for the current operation.",
-      "unsigned", "getNumInputs"
+      "This is an example of a non-static hook to an operation.",
+      "unsigned", "exampleInterfaceHook"
+    >,
+    StaticInterfaceMethod<
+      "This is an example of a static hook to an operation.",
+      "unsigned", "exampleStaticInterfaceHook"
     >,
   ];
 }
 ```
 
+Providing a definition of the `AttrInterface`, `OpInterface`, or `TypeInterface`
+class will auto-generate the C++ classes for the interface. Interfaces are
+comprised of the following components:
+
+*   C++ Class Name (Provided via template parameter)
+    -   The name of the C++ interface class.
+*   Description (`description`)
+    -   A string description of the interface, its invariants, example usages,
+        etc.
+*   C++ Namespace (`cppNamespace`)
+    -   The C++ namespace that the interface class should be generated in.
+*   Methods (`methods`)
+    -   The list of interface hook methods that are defined by the IR object.
+    -   The structure of these methods is defined below.
+*   Extra Class Declarations (Optional: `extraClassDeclaration`)
+    -   Additional C++ code that is generated in the declaration of the
+        interface class. This allows for defining methods and more on the user
+        facing interface class, that do not need to hook into the IR entity.
+
+`OpInterface` classes may additionally contain the following:
+
+*   Verifier (`verify`)
+    -   A C++ code block containing additional verification applied to the
+        operation that the interface is attached to.
+    -   The structure of this code block corresponds 1-1 with the structure of a
+        [`Trait::verifyTrait`](Traits.md) method.
+
+There are two types of methods that can be used with an interface,
+`InterfaceMethod` and `StaticInterfaceMethod`. They are both comprised of the
+same core components, with the distinction that `StaticInterfaceMethod` models a
+static method on the derived IR object.
+
+Interface methods are comprised of the following components:
+
+*   Description
+    -   A string description of this method, its invariants, example usages,
+        etc.
+*   ReturnType
+    -   A string corresponding to the C++ return type of the method.
+*   MethodName
+    -   A string corresponding to the C++ name of the method.
+*   Arguments (Optional)
+    -   A dag of strings that correspond to a C++ type and variable name
+        respectively.
+*   MethodBody (Optional)
+    -   An optional explicit implementation of the interface method.
+    -   This implementation is placed within the method defined on the `Model`
+        traits class, and is not defined by the `Trait` class that is attached
+        to the IR entity. More concretely, this body is only visible by the
+        interface class and does not affect the derived IR entity.
+    -   `ConcreteAttr`/`ConcreteOp`/`ConcreteType` is an implicitly defined
+        `typename` that can be used to refer to the type of the derived IR
+        entity currently being operated on.
+    -   In non-static methods, `$_op` and `$_self` may be used to refer to an
+        instance of the derived IR entity.
+*   DefaultImplementation (Optional)
+    -   An optional explicit default implementation of the interface method.
+    -   This implementation is placed within the `Trait` class that is attached
+        to the IR entity, and does not directly affect any of the interface
+        classes. As such, this method has the same characteristics as any other
+        [`Trait`](Traits.md) method.
+    -   `ConcreteAttr`/`ConcreteOp`/`ConcreteType` is an implicitly defined
+        `typename` that can be used to refer to the type of the derived IR
+        entity currently being operated on.
+
+ODS also allows for generating declarations for the `InterfaceMethod`s of an
+operation if the operation specifies the interface with
+`DeclareOpInterfaceMethods` (see an example below).
+
+Examples:
+
+~~~tablegen
+def MyInterface : OpInterface<"MyInterface"> {
+  let description = [{
+    This is the description of the interface. It provides concrete information
+    on the semantics of the interface, and how it may be used by the compiler.
+  }];
+
+  let methods = [
+    InterfaceMethod<[{
+      This method represents a simple non-static interface method with no
+      inputs, and a void return type. This method is required to be implemented
+      by all operations implementing this interface. This method roughly
+      correlates to the following on an operation implementing this interface:
+
+      ```c++
+      class ConcreteOp ... {
+      public:
+        void nonStaticMethod();
+      };
+      ```
+    }], "void", "nonStaticMethod"
+    >,
+
+    InterfaceMethod<[{
+      This method represents a non-static interface method with a non-void
+      return value, as well as an `unsigned` input named `i`. This method is
+      required to be implemented by all operations implementing this interface.
+      This method roughly correlates to the following on an operation
+      implementing this interface:
+
+      ```c++
+      class ConcreteOp ... {
+      public:
+        Value nonStaticMethod(unsigned i);
+      };
+      ```
+    }], "Value", "nonStaticMethodWithParams", (ins "unsigned":$i)
+    >,
+
+    StaticInterfaceMethod<[{
+      This method represents a static interface method with no inputs, and a
+      void return type. This method is required to be implemented by all
+      operations implementing this interface. This method roughly correlates
+      to the following on an operation implementing this interface:
+
+      ```c++
+      class ConcreteOp ... {
+      public:
+        static void staticMethod();
+      };
+      ```
+    }], "void", "staticMethod"
+    >,
+
+    StaticInterfaceMethod<[{
+      This method corresponds to a static interface method that has an explicit
+      implementation of the method body. Given that the method body has been
+      explicitly implemented, this method should not be defined by the operation
+      implementing this method. This method merely takes advantage of properties
+      already available on the operation, in this case its `build` methods. This
+      method roughly correlates to the following on the interface `Model` class:
+
+      ```c++
+      struct InterfaceTraits {
+        /// ... The `Concept` class is elided here ...
+
+        template <typename ConcreteOp>
+        struct Model : public Concept {
+          Operation *create(OpBuilder &builder, Location loc) const override {
+            return builder.create<ConcreteOp>(loc);
+          }
+        }
+      };
+      ```
+
+      Note above how no modification is required for operations implementing an
+      interface with this method.
+    }],
+      "Operation *", "create", (ins "OpBuilder &":$builder, "Location":$loc),
+      /*methodBody=*/[{
+        return builder.create<ConcreteOp>(loc);
+    }]>,
+
+    InterfaceMethod<[{
+      This method represents a non-static method that has an explicit
+      implementation of the method body. Given that the method body has been
+      explicitly implemented, this method should not be defined by the operation
+      implementing this method. This method merely takes advantage of properties
+      already available on the operation, in this case its `build` methods. This
+      method roughly correlates to the following on the interface `Model` class:
+
+      ```c++
+      struct InterfaceTraits {
+        /// ... The `Concept` class is elided here ...
+
+        template <typename ConcreteOp>
+        struct Model : public Concept {
+          Operation *create(Operation *opaqueOp, OpBuilder &builder,
+                            Location loc) const override {
+            ConcreteOp op = cast<ConcreteOp>(opaqueOp);
+            return op.getNumInputs() + op.getNumOutputs();
+          }
+        }
+      };
+      ```
+
+      Note above how no modification is required for operations implementing an
+      interface with this method.
+    }],
+      "unsigned", "getNumInputsAndOutputs", (ins), /*methodBody=*/[{
+        return $_op.getNumInputs() + $_op.getNumOutputs();
+    }]>,
+
+    InterfaceMethod<[{
+      This method represents a non-static method that has a default
+      implementation of the method body. This means that the implementation
+      defined here will be placed in the trait class that is attached to every
+      operation that implements this interface. This has no effect on the
+      generated `Concept` and `Model` class. This method roughly correlates to
+      the following on the interface `Trait` class:
+
+      ```c++
+      template <typename ConcreteOp>
+      class MyTrait : public OpTrait::TraitBase<ConcreteType, MyTrait> {
+      public:
+        bool isSafeToTransform() {
+          ConcreteOp op = cast<ConcreteOp>(this->getOperation());
+          return op.getNumInputs() + op.getNumOutputs();
+        }
+      };
+      ```
+
+      As detailed in [Traits](Traits.md), given that each operation implementing
+      this interface will also add the interface trait, the methods on this
+      interface are inherited by the derived operation. This allows for
+      injecting a default implementation of this method into each operation that
+      implements this interface, without changing the interface class itself. If
+      an operation wants to override this default implementation, it merely
+      needs to implement the method and the derived implementation will be
+      picked up transparently by the interface class.
+
+      ```c++
+      class ConcreteOp ... {
+      public:
+        bool isSafeToTransform() {
+          // Here we can override the default implementation of the hook
+          // provided by the trait.
+        }
+      };
+      ```
+    }],
+      "bool", "isSafeToTransform", (ins), /*methodBody=*/[{}],
+      /*defaultImplementation=*/[{
+    }]>,
+  ];
+}
+
+// Operation interfaces can optionally be wrapped inside
+// DeclareOpInterfaceMethods. This would result in autogenerating declarations
+// for members `foo`, `bar` and `fooStatic`. Methods with bodies are not
+// declared inside the op declaration but instead handled by the op interface
+// trait directly.
+def OpWithInferTypeInterfaceOp : Op<...
+    [DeclareOpInterfaceMethods<MyInterface>]> { ... }
+
+// Methods that have a default implementation do not have declarations
+// generated. If an operation wishes to override the default behavior, it can
+// explicitly specify the method that it wishes to override. This will force
+// the generation of a declaration for those methods.
+def OpWithOverrideInferTypeInterfaceOp : Op<...
+    [DeclareOpInterfaceMethods<MyInterface, ["getNumWithDefault"]>]> { ... }
+~~~
+
+Note: Existing operation interfaces defined in C++ can be accessed in the ODS
+framework via the `OpInterfaceTrait` class.
+
 #### Operation Interface List
 
-MLIR includes standard interfaces providing functionality that is
-likely to be common across many 
diff erent operations. Below is a list
-of some key interfaces that may be used directly by any dialect. The
-format of the header for each interface section goes as follows:
+MLIR includes standard interfaces providing functionality that is likely to be
+common across many 
diff erent operations. Below is a list of some key interfaces
+that may be used directly by any dialect. The format of the header for each
+interface section goes as follows:
 
 *   `Interface class name`
     -   (`C++ class` -- `ODS class`(if applicable))
@@ -224,10 +510,15 @@ format of the header for each interface section goes as follows:
 ##### RegionKindInterfaces
 
 *   `RegionKindInterface` - Used to describe the abstract semantics of regions.
-     - `RegionKind getRegionKind(unsigned index)` - Return the kind of the region with the given index inside this operation.
-         - RegionKind::Graph - represents a graph region without control flow semantics
-         - RegionKind::SSACFG - represents an [SSA-style control flow](LangRef.md#modeling-control-flow) region with basic blocks and reachability
-     - `hasSSADominance(unsigned index)` - Return true if the region with the given index inside this operation requires dominance.
+    -   `RegionKind getRegionKind(unsigned index)` - Return the kind of the
+        region with the given index inside this operation.
+        -   RegionKind::Graph - represents a graph region without control flow
+            semantics
+        -   RegionKind::SSACFG - represents an
+            [SSA-style control flow](LangRef.md#modeling-control-flow) region
+            with basic blocks and reachability
+    -   `hasSSADominance(unsigned index)` - Return true if the region with the
+        given index inside this operation requires dominance.
 
 ##### SymbolInterfaces
 

diff  --git a/mlir/docs/OpDefinitions.md b/mlir/docs/OpDefinitions.md
index d4f3f4a03eba..a267a60adc3e 100644
--- a/mlir/docs/OpDefinitions.md
+++ b/mlir/docs/OpDefinitions.md
@@ -340,134 +340,10 @@ currently only be specified as the last successor in the successor list.
 Traits are operation properties that affect syntax or semantics. MLIR C++
 models various traits in the `mlir::OpTrait` namespace.
 
-Both operation traits, [interfaces](#operation-interfaces), and constraints
-involving multiple operands/attributes/results are provided as the second
-template parameter to the `Op` class. They should be deriving from the `OpTrait`
-class. See [Constraints](#constraints) for more information.
-
-### Interfaces
-
-[Interfaces](Interfaces.md#attribute-operation-type-interfaces) allow for
-attributes, operations, and types to expose method calls without the caller
-needing to know the derived type. Operation interfaces defined in C++ can be
-accessed in the ODS framework via the `OpInterfaceTrait` class. Aside from using
-pre-existing interfaces in the C++ API, the ODS framework also provides a
-simplified mechanism for defining such interfaces which removes much of the
-boilerplate necessary.
-
-Providing a definition of the `AttrInterface`, `OpInterface`, or `TypeInterface`
-class will auto-generate the C++ classes for the interface. An interface
-includes a name, for the C++ class, a description, and a list of interface
-methods.
-
-```tablegen
-def MyInterface : OpInterface<"MyInterface"> {
-  let description = ...;
-  let methods = [...];
-}
-```
-
-There are two types of methods that can be used with an interface,
-`InterfaceMethod` and `StaticInterfaceMethod`. They are both comprised of the
-same core components, with the distinction that `StaticInterfaceMethod` models a
-static method on the derived operation.
-
-An `InterfaceMethod` is comprised of the following components:
-
-*   Description
-    -   A string description of what this method does and its invariants.
-*   ReturnType
-    -   A string corresponding to the C++ return type of the method.
-*   MethodName
-    -   A string corresponding to the desired name of the method.
-*   Arguments (Optional)
-    -   A dag of strings that correspond to a C++ type and variable name
-        respectively.
-*   MethodBody (Optional)
-    -   An optional explicit implementation of the interface method.
-    -   `ConcreteOp` is an implicitly defined typename that can be used to refer
-        to the type of the derived operation currently being operated on.
-    -   In non-static methods, a variable 'ConcreteOp op' is defined and may be
-        used to refer to an instance of the derived operation.
-*   DefaultImplementation (Optional)
-    -   An optional explicit default implementation of the interface method.
-    -   This method is placed within the `Trait` class that is attached to the
-        operation. As such, this method has the same characteristics as any
-        other [`Trait`](Traits.md) method.
-    -   `ConcreteOp` is an implicitly defined typename that can be used to refer
-        to the type of the derived operation currently being operated on.
-
-ODS also allows generating the declarations for the `InterfaceMethod` of the op
-if one specifies the interface with `DeclareOpInterfaceMethods` (see example
-below).
-
-Examples:
-
-```tablegen
-def MyInterface : OpInterface<"MyInterface"> {
-  let description = [{
-    My interface is very interesting. ...
-  }];
-
-  let methods = [
-    // A simple non-static method with no inputs.
-    InterfaceMethod<"'foo' is a non-static method with no inputs.",
-      "unsigned", "foo"
-    >,
-
-    // A new non-static method accepting an input argument.
-    InterfaceMethod<"/*insert doc here*/",
-      "Value ", "bar", (ins "unsigned":$i)
-    >,
-
-    // Query a static property of the derived operation.
-    StaticInterfaceMethod<"'fooStatic' is a static method with no inputs.",
-      "unsigned", "fooStatic"
-    >,
-
-    // Provide the definition of a static interface method.
-    // Note: `ConcreteOp` corresponds to the derived operation typename.
-    StaticInterfaceMethod<"/*insert doc here*/",
-      "Operation *", "create", (ins "OpBuilder &":$builder, "Location":$loc), [{
-        return builder.create<ConcreteOp>(loc);
-    }]>,
-
-    // Provide a definition of the non-static method.
-    // Note: `op` corresponds to the derived operation variable.
-    InterfaceMethod<"/*insert doc here*/",
-      "unsigned", "getNumInputsAndOutputs", (ins), [{
-        return op.getNumInputs() + op.getNumOutputs();
-    }]>,
-
-    // Provide only a default definition of the method.
-    // Note: `ConcreteOp` corresponds to the derived operation typename.
-    InterfaceMethod<"/*insert doc here*/",
-      "unsigned", "getNumWithDefault", (ins), /*methodBody=*/[{}], [{
-        ConcreteOp op = cast<ConcreteOp>(this->getOperation());
-        return op.getNumInputs() + op.getNumOutputs();
-    }]>,
-  ];
-}
-
-// Operation interfaces can optionally be wrapped inside
-// DeclareOpInterfaceMethods. This would result in autogenerating declarations
-// for members `foo`, `bar` and `fooStatic`. Methods with bodies are not
-// declared inside the op declaration but instead handled by the op interface
-// trait directly.
-def OpWithInferTypeInterfaceOp : Op<...
-    [DeclareOpInterfaceMethods<MyInterface>]> { ... }
-
-// Methods that have a default implementation do not have declarations
-// generated. If an operation wishes to override the default behavior, it can
-// explicitly specify the method that it wishes to override. This will force
-// the generation of a declaration for those methods.
-def OpWithOverrideInferTypeInterfaceOp : Op<...
-    [DeclareOpInterfaceMethods<MyInterface, ["getNumWithDefault"]>]> { ... }
-```
-
-Operation interfaces may also provide a verification method on `OpInterface` by
-setting `verify`. Setting `verify` results in the generated trait having a
-`verifyTrait` method that is applied to all operations implementing the trait.
+Both operation traits, [interfaces](Interfaces.md#utilizing-the-ods-framework),
+and constraints involving multiple operands/attributes/results are provided as
+the second template parameter to the `Op` class. They should be deriving from
+the `OpTrait` class. See [Constraints](#constraints) for more information.
 
 ### Builder methods
 


        


More information about the llvm-branch-commits mailing list