//===- IRDLLoading.cpp - IRDL dialect loading --------------------- C++ -*-===// // // This file is licensed 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 // //===----------------------------------------------------------------------===// // // Manages the loading of MLIR objects from IRDL operations. // //===----------------------------------------------------------------------===// #include "mlir/Dialect/IRDL/IRDLLoading.h" #include "mlir/Dialect/IRDL/IR/IRDL.h" #include "mlir/Dialect/IRDL/IR/IRDLInterfaces.h" #include "mlir/Dialect/IRDL/IRDLSymbols.h" #include "mlir/Dialect/IRDL/IRDLVerifiers.h" #include "mlir/IR/Attributes.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/ExtensibleDialect.h" #include "mlir/IR/OperationSupport.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/SMLoc.h" #include <numeric> usingnamespacemlir; usingnamespacemlir::irdl; /// Verify that the given list of parameters satisfy the given constraints. /// This encodes the logic of the verification method for attributes and types /// defined with IRDL. static LogicalResult irdlAttrOrTypeVerifier(function_ref<InFlightDiagnostic()> emitError, ArrayRef<Attribute> params, ArrayRef<std::unique_ptr<Constraint>> constraints, ArrayRef<size_t> paramConstraints) { … } /// Get the operand segment sizes from the attribute dictionary. LogicalResult getSegmentSizesFromAttr(Operation *op, StringRef elemName, StringRef attrName, unsigned numElements, ArrayRef<Variadicity> variadicities, SmallVectorImpl<int> &segmentSizes) { … } /// Compute the segment sizes of the given element (operands, results). /// If the operation has more than two non-single elements (optional or /// variadic), then get the segment sizes from the attribute dictionary. /// Otherwise, compute the segment sizes from the number of elements. /// `elemName` should be either `"operand"` or `"result"`. LogicalResult getSegmentSizes(Operation *op, StringRef elemName, StringRef attrName, unsigned numElements, ArrayRef<Variadicity> variadicities, SmallVectorImpl<int> &segmentSizes) { … } /// Compute the segment sizes of the given operands. /// If the operation has more than two non-single operands (optional or /// variadic), then get the segment sizes from the attribute dictionary. /// Otherwise, compute the segment sizes from the number of operands. LogicalResult getOperandSegmentSizes(Operation *op, ArrayRef<Variadicity> variadicities, SmallVectorImpl<int> &segmentSizes) { … } /// Compute the segment sizes of the given results. /// If the operation has more than two non-single results (optional or /// variadic), then get the segment sizes from the attribute dictionary. /// Otherwise, compute the segment sizes from the number of results. LogicalResult getResultSegmentSizes(Operation *op, ArrayRef<Variadicity> variadicities, SmallVectorImpl<int> &segmentSizes) { … } /// Verify that the given operation satisfies the given constraints. /// This encodes the logic of the verification method for operations defined /// with IRDL. static LogicalResult irdlOpVerifier( Operation *op, ConstraintVerifier &verifier, ArrayRef<size_t> operandConstrs, ArrayRef<Variadicity> operandVariadicity, ArrayRef<size_t> resultConstrs, ArrayRef<Variadicity> resultVariadicity, const DenseMap<StringAttr, size_t> &attributeConstrs) { … } static LogicalResult irdlRegionVerifier( Operation *op, ConstraintVerifier &verifier, ArrayRef<std::unique_ptr<RegionConstraint>> regionsConstraints) { … } llvm::unique_function<LogicalResult(Operation *) const> mlir::irdl::createVerifier( OperationOp op, const DenseMap<irdl::TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types, const DenseMap<irdl::AttributeOp, std::unique_ptr<DynamicAttrDefinition>> &attrs) { … } /// Define and load an operation represented by a `irdl.operation` /// operation. static WalkResult loadOperation( OperationOp op, ExtensibleDialect *dialect, const DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types, const DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>> &attrs) { … } /// Get the verifier of a type or attribute definition. /// Return nullptr if the definition is invalid. static DynamicAttrDefinition::VerifierFn getAttrOrTypeVerifier( Operation *attrOrTypeDef, ExtensibleDialect *dialect, DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types, DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>> &attrs) { … } /// Get the possible bases of a constraint. Return `true` if all bases can /// potentially be matched. /// A base is a type or an attribute definition. For instance, the base of /// `irdl.parametric "!builtin.complex"(...)` is `builtin.complex`. /// This function returns the following information through arguments: /// - `paramIds`: the set of type or attribute IDs that are used as bases. /// - `paramIrdlOps`: the set of IRDL operations that are used as bases. /// - `isIds`: the set of type or attribute IDs that are used in `irdl.is` /// constraints. static bool getBases(Operation *op, SmallPtrSet<TypeID, 4> ¶mIds, SmallPtrSet<Operation *, 4> ¶mIrdlOps, SmallPtrSet<TypeID, 4> &isIds) { … } /// Check that an any_of is in the subset IRDL can handle. /// IRDL uses a greedy algorithm to match constraints. This means that if we /// encounter an `any_of` with multiple constraints, we will match the first /// constraint that is satisfied. Thus, the order of constraints matter in /// `any_of` with our current algorithm. /// In order to make the order of constraints irrelevant, we require that /// all `any_of` constraint parameters are disjoint. For this, we check that /// the base parameters are all disjoints between `parametric` operations, and /// that they are disjoint between `parametric` and `is` operations. /// This restriction will be relaxed in the future, when we will change our /// algorithm to be non-greedy. static LogicalResult checkCorrectAnyOf(AnyOfOp anyOf) { … } /// Load all dialects in the given module, without loading any operation, type /// or attribute definitions. static DenseMap<DialectOp, ExtensibleDialect *> loadEmptyDialects(ModuleOp op) { … } /// Preallocate type definitions objects with empty verifiers. /// This in particular allocates a TypeID for each type definition. static DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> preallocateTypeDefs(ModuleOp op, DenseMap<DialectOp, ExtensibleDialect *> dialects) { … } /// Preallocate attribute definitions objects with empty verifiers. /// This in particular allocates a TypeID for each attribute definition. static DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>> preallocateAttrDefs(ModuleOp op, DenseMap<DialectOp, ExtensibleDialect *> dialects) { … } LogicalResult mlir::irdl::loadDialects(ModuleOp op) { … }