//===- OperationSupport.h ---------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines a number of support types that Operation and related // classes build on top of. // //===----------------------------------------------------------------------===// #ifndef MLIR_IR_OPERATIONSUPPORT_H #define MLIR_IR_OPERATIONSUPPORT_H #include "mlir/IR/Attributes.h" #include "mlir/IR/BlockSupport.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/Diagnostics.h" #include "mlir/IR/DialectRegistry.h" #include "mlir/IR/Location.h" #include "mlir/IR/TypeRange.h" #include "mlir/IR/Types.h" #include "mlir/IR/Value.h" #include "mlir/Support/InterfaceSupport.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include "llvm/Support/TrailingObjects.h" #include #include namespace llvm { class BitVector; } // namespace llvm namespace mlir { class Dialect; class DictionaryAttr; class ElementsAttr; struct EmptyProperties; class MutableOperandRangeRange; class NamedAttrList; class Operation; struct OperationState; class OpAsmParser; class OpAsmPrinter; class OperandRange; class OperandRangeRange; class OpFoldResult; class Pattern; class Region; class ResultRange; class RewritePattern; class RewritePatternSet; class Type; class Value; class ValueRange; template class ValueTypeRange; //===----------------------------------------------------------------------===// // PropertyRef //===----------------------------------------------------------------------===// /// Type-safe wrapper around a void* for passing properties, including the /// properties structs of operations, generically through APIs. Pairs data with /// a TypeID for assert-based type checking. Note that the type in the type ID /// is the **storage** type of the property, and that the default object has a /// null data pointer and a type ID equal to the type ID for `void`. class PropertyRef { public: PropertyRef() = default; PropertyRef(TypeID typeID, void *data) : typeID(typeID), data(data) {} operator bool() const { return data != nullptr; } template Dest as() const { static_assert(std::is_pointer_v, "PropertyRef::as() requires T to be a pointer type"); assert((typeID == TypeID::get>>()) && "Property type mismatch: TypeID does not match requested type"); return static_cast(data); } TypeID getTypeID() const { return typeID; } private: TypeID typeID; void *data = nullptr; }; //===----------------------------------------------------------------------===// // OperationName //===----------------------------------------------------------------------===// class OperationName { public: using FoldHookFn = llvm::unique_function, SmallVectorImpl &) const>; using HasTraitFn = llvm::unique_function; using ParseAssemblyFn = llvm::unique_function; // Note: RegisteredOperationName is passed as reference here as the derived // class is defined below. using PopulateDefaultAttrsFn = llvm::unique_function; using PrintAssemblyFn = llvm::unique_function; using VerifyInvariantsFn = llvm::unique_function; using VerifyRegionInvariantsFn = llvm::unique_function; /// This class represents a type erased version of an operation. It contains /// all of the components necessary for opaquely interacting with an /// operation. If the operation is not registered, some of these components /// may not be populated. struct InterfaceConcept { virtual ~InterfaceConcept() = default; virtual LogicalResult foldHook(Operation *, ArrayRef, SmallVectorImpl &) = 0; virtual void getCanonicalizationPatterns(RewritePatternSet &, MLIRContext *) = 0; virtual bool hasTrait(TypeID) = 0; virtual OperationName::ParseAssemblyFn getParseAssemblyFn() = 0; virtual void populateDefaultAttrs(const OperationName &, NamedAttrList &) = 0; virtual void printAssembly(Operation *, OpAsmPrinter &, StringRef) = 0; virtual LogicalResult verifyInvariants(Operation *) = 0; virtual LogicalResult verifyRegionInvariants(Operation *) = 0; /// Implementation for properties virtual std::optional getInherentAttr(Operation *, StringRef name) = 0; virtual void setInherentAttr(Operation *op, StringAttr name, Attribute value) = 0; virtual void populateInherentAttrs(Operation *op, NamedAttrList &attrs) = 0; virtual LogicalResult verifyInherentAttrs(OperationName opName, NamedAttrList &attributes, function_ref emitError) = 0; virtual int getOpPropertyByteSize() = 0; virtual void initProperties(OperationName opName, PropertyRef storage, PropertyRef init) = 0; virtual void deleteProperties(PropertyRef) = 0; virtual void populateDefaultProperties(OperationName opName, PropertyRef properties) = 0; virtual LogicalResult setPropertiesFromAttr(OperationName, PropertyRef, Attribute, function_ref emitError) = 0; virtual Attribute getPropertiesAsAttr(Operation *) = 0; virtual void copyProperties(PropertyRef, PropertyRef) = 0; virtual bool compareProperties(PropertyRef, PropertyRef) = 0; virtual llvm::hash_code hashProperties(PropertyRef) = 0; }; public: class Impl : public InterfaceConcept { public: Impl(StringRef, Dialect *dialect, TypeID typeID, detail::InterfaceMap interfaceMap); Impl(StringAttr name, Dialect *dialect, TypeID typeID, detail::InterfaceMap interfaceMap) : name(name), typeID(typeID), dialect(dialect), interfaceMap(std::move(interfaceMap)) {} /// Returns true if this is a registered operation. bool isRegistered() const { return typeID != TypeID::get(); } detail::InterfaceMap &getInterfaceMap() { return interfaceMap; } Dialect *getDialect() const { return dialect; } StringAttr getName() const { return name; } TypeID getTypeID() const { return typeID; } TypeID getPropertiesTypeID() const { return propertiesTypeID; } ArrayRef getAttributeNames() const { return attributeNames; } protected: //===------------------------------------------------------------------===// // Registered Operation Info /// The name of the operation. StringAttr name; /// The unique identifier of the derived Op class. TypeID typeID; /// The following fields are only populated when the operation is /// registered. /// This is the dialect that this operation belongs to. Dialect *dialect; /// A map of interfaces that were registered to this operation. detail::InterfaceMap interfaceMap; /// A list of attribute names registered to this operation in StringAttr /// form. This allows for operation classes to use StringAttr for attribute /// lookup/creation/etc., as opposed to raw strings. ArrayRef attributeNames; /// The TypeID of the Properties struct for this operation. TypeID propertiesTypeID; friend class RegisteredOperationName; }; protected: /// Default implementation for unregistered operations. struct UnregisteredOpModel : public Impl { UnregisteredOpModel(StringAttr name, Dialect *dialect, TypeID typeID, detail::InterfaceMap interfaceMap) : Impl(name, dialect, typeID, std::move(interfaceMap)) { propertiesTypeID = TypeID::get(); } LogicalResult foldHook(Operation *, ArrayRef, SmallVectorImpl &) final; void getCanonicalizationPatterns(RewritePatternSet &, MLIRContext *) final; bool hasTrait(TypeID) final; OperationName::ParseAssemblyFn getParseAssemblyFn() final; void populateDefaultAttrs(const OperationName &, NamedAttrList &) final; void printAssembly(Operation *, OpAsmPrinter &, StringRef) final; LogicalResult verifyInvariants(Operation *) final; LogicalResult verifyRegionInvariants(Operation *) final; /// Implementation for properties std::optional getInherentAttr(Operation *op, StringRef name) final; void setInherentAttr(Operation *op, StringAttr name, Attribute value) final; void populateInherentAttrs(Operation *op, NamedAttrList &attrs) final; LogicalResult verifyInherentAttrs(OperationName opName, NamedAttrList &attributes, function_ref emitError) final; int getOpPropertyByteSize() final; void initProperties(OperationName opName, PropertyRef storage, PropertyRef init) final; void deleteProperties(PropertyRef) final; void populateDefaultProperties(OperationName opName, PropertyRef properties) final; LogicalResult setPropertiesFromAttr(OperationName, PropertyRef, Attribute, function_ref emitError) final; Attribute getPropertiesAsAttr(Operation *) final; void copyProperties(PropertyRef, PropertyRef) final; bool compareProperties(PropertyRef, PropertyRef) final; llvm::hash_code hashProperties(PropertyRef) final; }; public: OperationName(StringRef name, MLIRContext *context); /// Return if this operation is registered. bool isRegistered() const { return getImpl()->isRegistered(); } /// Return the unique identifier of the derived Op class, or null if not /// registered. TypeID getTypeID() const { return getImpl()->getTypeID(); } /// If this operation is registered, returns the registered information, /// std::nullopt otherwise. std::optional getRegisteredInfo() const; /// This hook implements a generalized folder for this operation. Operations /// can implement this to provide simplifications rules that are applied by /// the Builder::createOrFold API and the canonicalization pass. /// /// This is an intentionally limited interface - implementations of this /// hook can only perform the following changes to the operation: /// /// 1. They can leave the operation alone and without changing the IR, and /// return failure. /// 2. They can mutate the operation in place, without changing anything /// else in the IR. In this case, return success. /// 3. They can return a list of existing values that can be used instead /// of the operation. In this case, fill in the results list and return /// success. The caller will remove the operation and use those results /// instead. /// /// This allows expression of some simple in-place canonicalizations (e.g. /// "x+0 -> x", "min(x,y,x,z) -> min(x,y,z)", "x+y-x -> y", etc), as well as /// generalized constant folding. LogicalResult foldHook(Operation *op, ArrayRef operands, SmallVectorImpl &results) const { return getImpl()->foldHook(op, operands, results); } /// This hook returns any canonicalization pattern rewrites that the /// operation supports, for use by the canonicalization pass. void getCanonicalizationPatterns(RewritePatternSet &results, MLIRContext *context) const { return getImpl()->getCanonicalizationPatterns(results, context); } /// Returns true if the operation was registered with a particular trait, e.g. /// hasTrait(). Returns false if the operation /// is unregistered. template