xref: /llvm-project/mlir/lib/Dialect/IRDL/IRDLLoading.cpp (revision db791b278a414fb6df1acc1799adcf11d8fb9169)
18ac8c922SMathieu Fehr //===- IRDLLoading.cpp - IRDL dialect loading --------------------- C++ -*-===//
28ac8c922SMathieu Fehr //
38ac8c922SMathieu Fehr // This file is licensed under the Apache License v2.0 with LLVM Exceptions.
48ac8c922SMathieu Fehr // See https://llvm.org/LICENSE.txt for license information.
58ac8c922SMathieu Fehr // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68ac8c922SMathieu Fehr //
78ac8c922SMathieu Fehr //===----------------------------------------------------------------------===//
88ac8c922SMathieu Fehr //
98ac8c922SMathieu Fehr // Manages the loading of MLIR objects from IRDL operations.
108ac8c922SMathieu Fehr //
118ac8c922SMathieu Fehr //===----------------------------------------------------------------------===//
128ac8c922SMathieu Fehr 
138ac8c922SMathieu Fehr #include "mlir/Dialect/IRDL/IRDLLoading.h"
148ac8c922SMathieu Fehr #include "mlir/Dialect/IRDL/IR/IRDL.h"
15c8a581c3SMathieu Fehr #include "mlir/Dialect/IRDL/IR/IRDLInterfaces.h"
16*b86a9c5bSThéo Degioanni #include "mlir/Dialect/IRDL/IRDLSymbols.h"
17d4bde696SDaniil Dudkin #include "mlir/Dialect/IRDL/IRDLVerifiers.h"
18f1a04029SDaniil Dudkin #include "mlir/IR/Attributes.h"
198ac8c922SMathieu Fehr #include "mlir/IR/BuiltinOps.h"
208ac8c922SMathieu Fehr #include "mlir/IR/ExtensibleDialect.h"
21f1a04029SDaniil Dudkin #include "mlir/IR/OperationSupport.h"
228ac8c922SMathieu Fehr #include "llvm/ADT/STLExtras.h"
238ac8c922SMathieu Fehr #include "llvm/ADT/SmallPtrSet.h"
248ac8c922SMathieu Fehr #include "llvm/Support/SMLoc.h"
251295a351SMathieu Fehr #include <numeric>
268ac8c922SMathieu Fehr 
278ac8c922SMathieu Fehr using namespace mlir;
288ac8c922SMathieu Fehr using namespace mlir::irdl;
298ac8c922SMathieu Fehr 
30c8a581c3SMathieu Fehr /// Verify that the given list of parameters satisfy the given constraints.
31c8a581c3SMathieu Fehr /// This encodes the logic of the verification method for attributes and types
32c8a581c3SMathieu Fehr /// defined with IRDL.
33c8a581c3SMathieu Fehr static LogicalResult
irdlAttrOrTypeVerifier(function_ref<InFlightDiagnostic ()> emitError,ArrayRef<Attribute> params,ArrayRef<std::unique_ptr<Constraint>> constraints,ArrayRef<size_t> paramConstraints)34c8a581c3SMathieu Fehr irdlAttrOrTypeVerifier(function_ref<InFlightDiagnostic()> emitError,
35c8a581c3SMathieu Fehr                        ArrayRef<Attribute> params,
36c8a581c3SMathieu Fehr                        ArrayRef<std::unique_ptr<Constraint>> constraints,
37c8a581c3SMathieu Fehr                        ArrayRef<size_t> paramConstraints) {
38c8a581c3SMathieu Fehr   if (params.size() != paramConstraints.size()) {
39c8a581c3SMathieu Fehr     emitError() << "expected " << paramConstraints.size()
40c8a581c3SMathieu Fehr                 << " type arguments, but had " << params.size();
41c8a581c3SMathieu Fehr     return failure();
42c8a581c3SMathieu Fehr   }
43c8a581c3SMathieu Fehr 
44c8a581c3SMathieu Fehr   ConstraintVerifier verifier(constraints);
45c8a581c3SMathieu Fehr 
46c8a581c3SMathieu Fehr   // Check that each parameter satisfies its constraint.
47c8a581c3SMathieu Fehr   for (auto [i, param] : enumerate(params))
48c8a581c3SMathieu Fehr     if (failed(verifier.verify(emitError, param, paramConstraints[i])))
49c8a581c3SMathieu Fehr       return failure();
50c8a581c3SMathieu Fehr 
51c8a581c3SMathieu Fehr   return success();
52c8a581c3SMathieu Fehr }
53c8a581c3SMathieu Fehr 
541295a351SMathieu Fehr /// Get the operand segment sizes from the attribute dictionary.
getSegmentSizesFromAttr(Operation * op,StringRef elemName,StringRef attrName,unsigned numElements,ArrayRef<Variadicity> variadicities,SmallVectorImpl<int> & segmentSizes)551295a351SMathieu Fehr LogicalResult getSegmentSizesFromAttr(Operation *op, StringRef elemName,
561295a351SMathieu Fehr                                       StringRef attrName, unsigned numElements,
571295a351SMathieu Fehr                                       ArrayRef<Variadicity> variadicities,
581295a351SMathieu Fehr                                       SmallVectorImpl<int> &segmentSizes) {
591295a351SMathieu Fehr   // Get the segment sizes attribute, and check that it is of the right type.
601295a351SMathieu Fehr   Attribute segmentSizesAttr = op->getAttr(attrName);
611295a351SMathieu Fehr   if (!segmentSizesAttr) {
621295a351SMathieu Fehr     return op->emitError() << "'" << attrName
631295a351SMathieu Fehr                            << "' attribute is expected but not provided";
641295a351SMathieu Fehr   }
651295a351SMathieu Fehr 
661295a351SMathieu Fehr   auto denseSegmentSizes = dyn_cast<DenseI32ArrayAttr>(segmentSizesAttr);
671295a351SMathieu Fehr   if (!denseSegmentSizes) {
681295a351SMathieu Fehr     return op->emitError() << "'" << attrName
691295a351SMathieu Fehr                            << "' attribute is expected to be a dense i32 array";
701295a351SMathieu Fehr   }
711295a351SMathieu Fehr 
721295a351SMathieu Fehr   if (denseSegmentSizes.size() != (int64_t)variadicities.size()) {
731295a351SMathieu Fehr     return op->emitError() << "'" << attrName << "' attribute for specifying "
741295a351SMathieu Fehr                            << elemName << " segments must have "
751295a351SMathieu Fehr                            << variadicities.size() << " elements, but got "
761295a351SMathieu Fehr                            << denseSegmentSizes.size();
771295a351SMathieu Fehr   }
781295a351SMathieu Fehr 
791295a351SMathieu Fehr   // Check that the segment sizes are corresponding to the given variadicities,
801295a351SMathieu Fehr   for (auto [i, segmentSize, variadicity] :
811295a351SMathieu Fehr        enumerate(denseSegmentSizes.asArrayRef(), variadicities)) {
821295a351SMathieu Fehr     if (segmentSize < 0)
831295a351SMathieu Fehr       return op->emitError()
841295a351SMathieu Fehr              << "'" << attrName << "' attribute for specifying " << elemName
851295a351SMathieu Fehr              << " segments must have non-negative values";
861295a351SMathieu Fehr     if (variadicity == Variadicity::single && segmentSize != 1)
871295a351SMathieu Fehr       return op->emitError() << "element " << i << " in '" << attrName
881295a351SMathieu Fehr                              << "' attribute must be equal to 1";
891295a351SMathieu Fehr 
901295a351SMathieu Fehr     if (variadicity == Variadicity::optional && segmentSize > 1)
911295a351SMathieu Fehr       return op->emitError() << "element " << i << " in '" << attrName
921295a351SMathieu Fehr                              << "' attribute must be equal to 0 or 1";
931295a351SMathieu Fehr 
941295a351SMathieu Fehr     segmentSizes.push_back(segmentSize);
951295a351SMathieu Fehr   }
961295a351SMathieu Fehr 
971295a351SMathieu Fehr   // Check that the sum of the segment sizes is equal to the number of elements.
981295a351SMathieu Fehr   int32_t sum = 0;
991295a351SMathieu Fehr   for (int32_t segmentSize : denseSegmentSizes.asArrayRef())
1001295a351SMathieu Fehr     sum += segmentSize;
1011295a351SMathieu Fehr   if (sum != static_cast<int32_t>(numElements))
1021295a351SMathieu Fehr     return op->emitError() << "sum of elements in '" << attrName
1031295a351SMathieu Fehr                            << "' attribute must be equal to the number of "
1041295a351SMathieu Fehr                            << elemName << "s";
1051295a351SMathieu Fehr 
1061295a351SMathieu Fehr   return success();
1071295a351SMathieu Fehr }
1081295a351SMathieu Fehr 
1091295a351SMathieu Fehr /// Compute the segment sizes of the given element (operands, results).
1101295a351SMathieu Fehr /// If the operation has more than two non-single elements (optional or
1111295a351SMathieu Fehr /// variadic), then get the segment sizes from the attribute dictionary.
1121295a351SMathieu Fehr /// Otherwise, compute the segment sizes from the number of elements.
1131295a351SMathieu Fehr /// `elemName` should be either `"operand"` or `"result"`.
getSegmentSizes(Operation * op,StringRef elemName,StringRef attrName,unsigned numElements,ArrayRef<Variadicity> variadicities,SmallVectorImpl<int> & segmentSizes)1141295a351SMathieu Fehr LogicalResult getSegmentSizes(Operation *op, StringRef elemName,
1151295a351SMathieu Fehr                               StringRef attrName, unsigned numElements,
1161295a351SMathieu Fehr                               ArrayRef<Variadicity> variadicities,
1171295a351SMathieu Fehr                               SmallVectorImpl<int> &segmentSizes) {
1181295a351SMathieu Fehr   // If we have more than one non-single variadicity, we need to get the
1191295a351SMathieu Fehr   // segment sizes from the attribute dictionary.
1201295a351SMathieu Fehr   int numberNonSingle = count_if(
1211295a351SMathieu Fehr       variadicities, [](Variadicity v) { return v != Variadicity::single; });
1221295a351SMathieu Fehr   if (numberNonSingle > 1)
1231295a351SMathieu Fehr     return getSegmentSizesFromAttr(op, elemName, attrName, numElements,
1241295a351SMathieu Fehr                                    variadicities, segmentSizes);
1251295a351SMathieu Fehr 
1261295a351SMathieu Fehr   // If we only have single variadicities, the segments sizes are all 1.
1271295a351SMathieu Fehr   if (numberNonSingle == 0) {
1281295a351SMathieu Fehr     if (numElements != variadicities.size()) {
1291295a351SMathieu Fehr       return op->emitError() << "op expects exactly " << variadicities.size()
1301295a351SMathieu Fehr                              << " " << elemName << "s, but got " << numElements;
1311295a351SMathieu Fehr     }
1321295a351SMathieu Fehr     for (size_t i = 0, e = variadicities.size(); i < e; ++i)
1331295a351SMathieu Fehr       segmentSizes.push_back(1);
1341295a351SMathieu Fehr     return success();
1351295a351SMathieu Fehr   }
1361295a351SMathieu Fehr 
1371295a351SMathieu Fehr   assert(numberNonSingle == 1);
1381295a351SMathieu Fehr 
1391295a351SMathieu Fehr   // There is exactly one non-single element, so we can
1401295a351SMathieu Fehr   // compute its size and check that it is valid.
1411295a351SMathieu Fehr   int nonSingleSegmentSize = static_cast<int>(numElements) -
1421295a351SMathieu Fehr                              static_cast<int>(variadicities.size()) + 1;
1431295a351SMathieu Fehr 
1441295a351SMathieu Fehr   if (nonSingleSegmentSize < 0) {
1451295a351SMathieu Fehr     return op->emitError() << "op expects at least " << variadicities.size() - 1
1461295a351SMathieu Fehr                            << " " << elemName << "s, but got " << numElements;
1471295a351SMathieu Fehr   }
1481295a351SMathieu Fehr 
1491295a351SMathieu Fehr   // Add the segment sizes.
1501295a351SMathieu Fehr   for (Variadicity variadicity : variadicities) {
1511295a351SMathieu Fehr     if (variadicity == Variadicity::single) {
1521295a351SMathieu Fehr       segmentSizes.push_back(1);
1531295a351SMathieu Fehr       continue;
1541295a351SMathieu Fehr     }
1551295a351SMathieu Fehr 
1561295a351SMathieu Fehr     // If we have an optional element, we should check that it represents
1571295a351SMathieu Fehr     // zero or one elements.
1581295a351SMathieu Fehr     if (nonSingleSegmentSize > 1 && variadicity == Variadicity::optional)
1591295a351SMathieu Fehr       return op->emitError() << "op expects at most " << variadicities.size()
1601295a351SMathieu Fehr                              << " " << elemName << "s, but got " << numElements;
1611295a351SMathieu Fehr 
1621295a351SMathieu Fehr     segmentSizes.push_back(nonSingleSegmentSize);
1631295a351SMathieu Fehr   }
1641295a351SMathieu Fehr 
1651295a351SMathieu Fehr   return success();
1661295a351SMathieu Fehr }
1671295a351SMathieu Fehr 
1681295a351SMathieu Fehr /// Compute the segment sizes of the given operands.
1691295a351SMathieu Fehr /// If the operation has more than two non-single operands (optional or
1701295a351SMathieu Fehr /// variadic), then get the segment sizes from the attribute dictionary.
1711295a351SMathieu Fehr /// Otherwise, compute the segment sizes from the number of operands.
getOperandSegmentSizes(Operation * op,ArrayRef<Variadicity> variadicities,SmallVectorImpl<int> & segmentSizes)1721295a351SMathieu Fehr LogicalResult getOperandSegmentSizes(Operation *op,
1731295a351SMathieu Fehr                                      ArrayRef<Variadicity> variadicities,
1741295a351SMathieu Fehr                                      SmallVectorImpl<int> &segmentSizes) {
1751295a351SMathieu Fehr   return getSegmentSizes(op, "operand", "operand_segment_sizes",
1761295a351SMathieu Fehr                          op->getNumOperands(), variadicities, segmentSizes);
1771295a351SMathieu Fehr }
1781295a351SMathieu Fehr 
1791295a351SMathieu Fehr /// Compute the segment sizes of the given results.
1801295a351SMathieu Fehr /// If the operation has more than two non-single results (optional or
1811295a351SMathieu Fehr /// variadic), then get the segment sizes from the attribute dictionary.
1821295a351SMathieu Fehr /// Otherwise, compute the segment sizes from the number of results.
getResultSegmentSizes(Operation * op,ArrayRef<Variadicity> variadicities,SmallVectorImpl<int> & segmentSizes)1831295a351SMathieu Fehr LogicalResult getResultSegmentSizes(Operation *op,
1841295a351SMathieu Fehr                                     ArrayRef<Variadicity> variadicities,
1851295a351SMathieu Fehr                                     SmallVectorImpl<int> &segmentSizes) {
1861295a351SMathieu Fehr   return getSegmentSizes(op, "result", "result_segment_sizes",
1871295a351SMathieu Fehr                          op->getNumResults(), variadicities, segmentSizes);
1881295a351SMathieu Fehr }
1891295a351SMathieu Fehr 
190c8a581c3SMathieu Fehr /// Verify that the given operation satisfies the given constraints.
191c8a581c3SMathieu Fehr /// This encodes the logic of the verification method for operations defined
192c8a581c3SMathieu Fehr /// with IRDL.
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)1931295a351SMathieu Fehr static LogicalResult irdlOpVerifier(
194d4bde696SDaniil Dudkin     Operation *op, ConstraintVerifier &verifier,
1951295a351SMathieu Fehr     ArrayRef<size_t> operandConstrs, ArrayRef<Variadicity> operandVariadicity,
1961295a351SMathieu Fehr     ArrayRef<size_t> resultConstrs, ArrayRef<Variadicity> resultVariadicity,
197f1a04029SDaniil Dudkin     const DenseMap<StringAttr, size_t> &attributeConstrs) {
1981295a351SMathieu Fehr   // Get the segment sizes for the operands.
1991295a351SMathieu Fehr   // This will check that the number of operands is correct.
2001295a351SMathieu Fehr   SmallVector<int> operandSegmentSizes;
2011295a351SMathieu Fehr   if (failed(
2021295a351SMathieu Fehr           getOperandSegmentSizes(op, operandVariadicity, operandSegmentSizes)))
2031295a351SMathieu Fehr     return failure();
204c8a581c3SMathieu Fehr 
2051295a351SMathieu Fehr   // Get the segment sizes for the results.
2061295a351SMathieu Fehr   // This will check that the number of results is correct.
2071295a351SMathieu Fehr   SmallVector<int> resultSegmentSizes;
2081295a351SMathieu Fehr   if (failed(getResultSegmentSizes(op, resultVariadicity, resultSegmentSizes)))
2091295a351SMathieu Fehr     return failure();
210c8a581c3SMathieu Fehr 
211f1a04029SDaniil Dudkin   auto emitError = [op] { return op->emitError(); };
212c8a581c3SMathieu Fehr 
213f1a04029SDaniil Dudkin   /// Сheck that we have all needed attributes passed
214f1a04029SDaniil Dudkin   /// and they satisfy the constraints.
215f1a04029SDaniil Dudkin   DictionaryAttr actualAttrs = op->getAttrDictionary();
216f1a04029SDaniil Dudkin 
217f1a04029SDaniil Dudkin   for (auto [name, constraint] : attributeConstrs) {
218f1a04029SDaniil Dudkin     /// First, check if the attribute actually passed.
219f1a04029SDaniil Dudkin     std::optional<NamedAttribute> actual = actualAttrs.getNamed(name);
220f1a04029SDaniil Dudkin     if (!actual.has_value())
221f1a04029SDaniil Dudkin       return op->emitOpError()
222f1a04029SDaniil Dudkin              << "attribute " << name << " is expected but not provided";
223f1a04029SDaniil Dudkin 
224f1a04029SDaniil Dudkin     /// Then, check if the attribute value satisfies the constraint.
225f1a04029SDaniil Dudkin     if (failed(verifier.verify({emitError}, actual->getValue(), constraint)))
226f1a04029SDaniil Dudkin       return failure();
227f1a04029SDaniil Dudkin   }
228f1a04029SDaniil Dudkin 
2291295a351SMathieu Fehr   // Check that all operands satisfy the constraints
2301295a351SMathieu Fehr   int operandIdx = 0;
2311295a351SMathieu Fehr   for (auto [defIndex, segmentSize] : enumerate(operandSegmentSizes)) {
2321295a351SMathieu Fehr     for (int i = 0; i < segmentSize; i++) {
2331295a351SMathieu Fehr       if (failed(verifier.verify(
2341295a351SMathieu Fehr               {emitError}, TypeAttr::get(op->getOperandTypes()[operandIdx]),
2351295a351SMathieu Fehr               operandConstrs[defIndex])))
236c8a581c3SMathieu Fehr         return failure();
2371295a351SMathieu Fehr       ++operandIdx;
2381295a351SMathieu Fehr     }
2391295a351SMathieu Fehr   }
240c8a581c3SMathieu Fehr 
2411295a351SMathieu Fehr   // Check that all results satisfy the constraints
2421295a351SMathieu Fehr   int resultIdx = 0;
2431295a351SMathieu Fehr   for (auto [defIndex, segmentSize] : enumerate(resultSegmentSizes)) {
2441295a351SMathieu Fehr     for (int i = 0; i < segmentSize; i++) {
2451295a351SMathieu Fehr       if (failed(verifier.verify({emitError},
2461295a351SMathieu Fehr                                  TypeAttr::get(op->getResultTypes()[resultIdx]),
2471295a351SMathieu Fehr                                  resultConstrs[defIndex])))
248c8a581c3SMathieu Fehr         return failure();
2491295a351SMathieu Fehr       ++resultIdx;
2501295a351SMathieu Fehr     }
2511295a351SMathieu Fehr   }
252c8a581c3SMathieu Fehr 
253c8a581c3SMathieu Fehr   return success();
254c8a581c3SMathieu Fehr }
255c8a581c3SMathieu Fehr 
irdlRegionVerifier(Operation * op,ConstraintVerifier & verifier,ArrayRef<std::unique_ptr<RegionConstraint>> regionsConstraints)256d4bde696SDaniil Dudkin static LogicalResult irdlRegionVerifier(
257d4bde696SDaniil Dudkin     Operation *op, ConstraintVerifier &verifier,
258d4bde696SDaniil Dudkin     ArrayRef<std::unique_ptr<RegionConstraint>> regionsConstraints) {
259d4bde696SDaniil Dudkin   if (op->getNumRegions() != regionsConstraints.size()) {
260d4bde696SDaniil Dudkin     return op->emitOpError()
261d4bde696SDaniil Dudkin            << "unexpected number of regions: expected "
262d4bde696SDaniil Dudkin            << regionsConstraints.size() << " but got " << op->getNumRegions();
263d4bde696SDaniil Dudkin   }
264d4bde696SDaniil Dudkin 
265d4bde696SDaniil Dudkin   for (auto [constraint, region] :
266d4bde696SDaniil Dudkin        llvm::zip(regionsConstraints, op->getRegions()))
267d4bde696SDaniil Dudkin     if (failed(constraint->verify(region, verifier)))
268d4bde696SDaniil Dudkin       return failure();
269d4bde696SDaniil Dudkin 
270d4bde696SDaniil Dudkin   return success();
271d4bde696SDaniil Dudkin }
272d4bde696SDaniil Dudkin 
273105c992cSOleksandr "Alex" Zinenko llvm::unique_function<LogicalResult(Operation *) const>
createVerifier(OperationOp op,const DenseMap<irdl::TypeOp,std::unique_ptr<DynamicTypeDefinition>> & types,const DenseMap<irdl::AttributeOp,std::unique_ptr<DynamicAttrDefinition>> & attrs)274105c992cSOleksandr "Alex" Zinenko mlir::irdl::createVerifier(
275105c992cSOleksandr "Alex" Zinenko     OperationOp op,
276105c992cSOleksandr "Alex" Zinenko     const DenseMap<irdl::TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types,
277105c992cSOleksandr "Alex" Zinenko     const DenseMap<irdl::AttributeOp, std::unique_ptr<DynamicAttrDefinition>>
278105c992cSOleksandr "Alex" Zinenko         &attrs) {
279c8a581c3SMathieu Fehr   // Resolve SSA values to verifier constraint slots
280c8a581c3SMathieu Fehr   SmallVector<Value> constrToValue;
281d4bde696SDaniil Dudkin   SmallVector<Value> regionToValue;
282c8a581c3SMathieu Fehr   for (Operation &op : op->getRegion(0).getOps()) {
283c8a581c3SMathieu Fehr     if (isa<VerifyConstraintInterface>(op)) {
284105c992cSOleksandr "Alex" Zinenko       if (op.getNumResults() != 1) {
285105c992cSOleksandr "Alex" Zinenko         op.emitError()
286c8a581c3SMathieu Fehr             << "IRDL constraint operations must have exactly one result";
287105c992cSOleksandr "Alex" Zinenko         return nullptr;
288105c992cSOleksandr "Alex" Zinenko       }
289c8a581c3SMathieu Fehr       constrToValue.push_back(op.getResult(0));
290c8a581c3SMathieu Fehr     }
291d4bde696SDaniil Dudkin     if (isa<VerifyRegionInterface>(op)) {
292105c992cSOleksandr "Alex" Zinenko       if (op.getNumResults() != 1) {
293105c992cSOleksandr "Alex" Zinenko         op.emitError()
294d4bde696SDaniil Dudkin             << "IRDL constraint operations must have exactly one result";
295105c992cSOleksandr "Alex" Zinenko         return nullptr;
296105c992cSOleksandr "Alex" Zinenko       }
297d4bde696SDaniil Dudkin       regionToValue.push_back(op.getResult(0));
298d4bde696SDaniil Dudkin     }
299c8a581c3SMathieu Fehr   }
300c8a581c3SMathieu Fehr 
301c8a581c3SMathieu Fehr   // Build the verifiers for each constraint slot
302c8a581c3SMathieu Fehr   SmallVector<std::unique_ptr<Constraint>> constraints;
303c8a581c3SMathieu Fehr   for (Value v : constrToValue) {
304c8a581c3SMathieu Fehr     VerifyConstraintInterface op =
305c8a581c3SMathieu Fehr         cast<VerifyConstraintInterface>(v.getDefiningOp());
306c8a581c3SMathieu Fehr     std::unique_ptr<Constraint> verifier =
307c8a581c3SMathieu Fehr         op.getVerifier(constrToValue, types, attrs);
308c8a581c3SMathieu Fehr     if (!verifier)
309105c992cSOleksandr "Alex" Zinenko       return nullptr;
310c8a581c3SMathieu Fehr     constraints.push_back(std::move(verifier));
311c8a581c3SMathieu Fehr   }
312c8a581c3SMathieu Fehr 
313d4bde696SDaniil Dudkin   // Build region constraints
314d4bde696SDaniil Dudkin   SmallVector<std::unique_ptr<RegionConstraint>> regionConstraints;
315d4bde696SDaniil Dudkin   for (Value v : regionToValue) {
316d4bde696SDaniil Dudkin     VerifyRegionInterface op = cast<VerifyRegionInterface>(v.getDefiningOp());
317d4bde696SDaniil Dudkin     std::unique_ptr<RegionConstraint> verifier =
318d4bde696SDaniil Dudkin         op.getVerifier(constrToValue, types, attrs);
319d4bde696SDaniil Dudkin     regionConstraints.push_back(std::move(verifier));
320d4bde696SDaniil Dudkin   }
321d4bde696SDaniil Dudkin 
322c8a581c3SMathieu Fehr   SmallVector<size_t> operandConstraints;
3231295a351SMathieu Fehr   SmallVector<Variadicity> operandVariadicity;
324c8a581c3SMathieu Fehr 
325c8a581c3SMathieu Fehr   // Gather which constraint slots correspond to operand constraints
326c8a581c3SMathieu Fehr   auto operandsOp = op.getOp<OperandsOp>();
327c8a581c3SMathieu Fehr   if (operandsOp.has_value()) {
328c8a581c3SMathieu Fehr     operandConstraints.reserve(operandsOp->getArgs().size());
329c8a581c3SMathieu Fehr     for (Value operand : operandsOp->getArgs()) {
330c8a581c3SMathieu Fehr       for (auto [i, constr] : enumerate(constrToValue)) {
331c8a581c3SMathieu Fehr         if (constr == operand) {
332c8a581c3SMathieu Fehr           operandConstraints.push_back(i);
333c8a581c3SMathieu Fehr           break;
334c8a581c3SMathieu Fehr         }
335c8a581c3SMathieu Fehr       }
336c8a581c3SMathieu Fehr     }
3371295a351SMathieu Fehr 
3381295a351SMathieu Fehr     // Gather the variadicities of each operand
3391295a351SMathieu Fehr     for (VariadicityAttr attr : operandsOp->getVariadicity())
3401295a351SMathieu Fehr       operandVariadicity.push_back(attr.getValue());
341c8a581c3SMathieu Fehr   }
342c8a581c3SMathieu Fehr 
3431295a351SMathieu Fehr   SmallVector<size_t> resultConstraints;
3441295a351SMathieu Fehr   SmallVector<Variadicity> resultVariadicity;
3451295a351SMathieu Fehr 
346c8a581c3SMathieu Fehr   // Gather which constraint slots correspond to result constraints
347c8a581c3SMathieu Fehr   auto resultsOp = op.getOp<ResultsOp>();
348c8a581c3SMathieu Fehr   if (resultsOp.has_value()) {
349c8a581c3SMathieu Fehr     resultConstraints.reserve(resultsOp->getArgs().size());
350c8a581c3SMathieu Fehr     for (Value result : resultsOp->getArgs()) {
351c8a581c3SMathieu Fehr       for (auto [i, constr] : enumerate(constrToValue)) {
352c8a581c3SMathieu Fehr         if (constr == result) {
353c8a581c3SMathieu Fehr           resultConstraints.push_back(i);
354c8a581c3SMathieu Fehr           break;
355c8a581c3SMathieu Fehr         }
356c8a581c3SMathieu Fehr       }
357c8a581c3SMathieu Fehr     }
3581295a351SMathieu Fehr 
3591295a351SMathieu Fehr     // Gather the variadicities of each result
3601295a351SMathieu Fehr     for (Attribute attr : resultsOp->getVariadicity())
361a5757c5bSChristian Sigg       resultVariadicity.push_back(cast<VariadicityAttr>(attr).getValue());
362c8a581c3SMathieu Fehr   }
363c8a581c3SMathieu Fehr 
364f1a04029SDaniil Dudkin   // Gather which constraint slots correspond to attributes constraints
365105c992cSOleksandr "Alex" Zinenko   DenseMap<StringAttr, size_t> attributeConstraints;
366f1a04029SDaniil Dudkin   auto attributesOp = op.getOp<AttributesOp>();
367f1a04029SDaniil Dudkin   if (attributesOp.has_value()) {
368f1a04029SDaniil Dudkin     const Operation::operand_range values = attributesOp->getAttributeValues();
369f1a04029SDaniil Dudkin     const ArrayAttr names = attributesOp->getAttributeValueNames();
370f1a04029SDaniil Dudkin 
371f1a04029SDaniil Dudkin     for (const auto &[name, value] : llvm::zip(names, values)) {
372f1a04029SDaniil Dudkin       for (auto [i, constr] : enumerate(constrToValue)) {
373f1a04029SDaniil Dudkin         if (constr == value) {
374105c992cSOleksandr "Alex" Zinenko           attributeConstraints[cast<StringAttr>(name)] = i;
375f1a04029SDaniil Dudkin           break;
376f1a04029SDaniil Dudkin         }
377f1a04029SDaniil Dudkin       }
378f1a04029SDaniil Dudkin     }
379f1a04029SDaniil Dudkin   }
380f1a04029SDaniil Dudkin 
381105c992cSOleksandr "Alex" Zinenko   return
382105c992cSOleksandr "Alex" Zinenko       [constraints{std::move(constraints)},
383105c992cSOleksandr "Alex" Zinenko        regionConstraints{std::move(regionConstraints)},
384105c992cSOleksandr "Alex" Zinenko        operandConstraints{std::move(operandConstraints)},
385105c992cSOleksandr "Alex" Zinenko        operandVariadicity{std::move(operandVariadicity)},
386105c992cSOleksandr "Alex" Zinenko        resultConstraints{std::move(resultConstraints)},
387105c992cSOleksandr "Alex" Zinenko        resultVariadicity{std::move(resultVariadicity)},
388105c992cSOleksandr "Alex" Zinenko        attributeConstraints{std::move(attributeConstraints)}](Operation *op) {
389105c992cSOleksandr "Alex" Zinenko         ConstraintVerifier verifier(constraints);
390105c992cSOleksandr "Alex" Zinenko         const LogicalResult opVerifierResult = irdlOpVerifier(
391105c992cSOleksandr "Alex" Zinenko             op, verifier, operandConstraints, operandVariadicity,
392105c992cSOleksandr "Alex" Zinenko             resultConstraints, resultVariadicity, attributeConstraints);
393105c992cSOleksandr "Alex" Zinenko         const LogicalResult opRegionVerifierResult =
394105c992cSOleksandr "Alex" Zinenko             irdlRegionVerifier(op, verifier, regionConstraints);
395105c992cSOleksandr "Alex" Zinenko         return LogicalResult::success(opVerifierResult.succeeded() &&
396105c992cSOleksandr "Alex" Zinenko                                       opRegionVerifierResult.succeeded());
397105c992cSOleksandr "Alex" Zinenko       };
398105c992cSOleksandr "Alex" Zinenko }
399105c992cSOleksandr "Alex" Zinenko 
400105c992cSOleksandr "Alex" Zinenko /// Define and load an operation represented by a `irdl.operation`
401105c992cSOleksandr "Alex" Zinenko /// operation.
loadOperation(OperationOp op,ExtensibleDialect * dialect,const DenseMap<TypeOp,std::unique_ptr<DynamicTypeDefinition>> & types,const DenseMap<AttributeOp,std::unique_ptr<DynamicAttrDefinition>> & attrs)402105c992cSOleksandr "Alex" Zinenko static WalkResult loadOperation(
403105c992cSOleksandr "Alex" Zinenko     OperationOp op, ExtensibleDialect *dialect,
404105c992cSOleksandr "Alex" Zinenko     const DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types,
405105c992cSOleksandr "Alex" Zinenko     const DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>>
406105c992cSOleksandr "Alex" Zinenko         &attrs) {
407105c992cSOleksandr "Alex" Zinenko 
4088ac8c922SMathieu Fehr   // IRDL does not support defining custom parsers or printers.
4098ac8c922SMathieu Fehr   auto parser = [](OpAsmParser &parser, OperationState &result) {
4108ac8c922SMathieu Fehr     return failure();
4118ac8c922SMathieu Fehr   };
4128ac8c922SMathieu Fehr   auto printer = [](Operation *op, OpAsmPrinter &printer, StringRef) {
4138ac8c922SMathieu Fehr     printer.printGenericOp(op);
4148ac8c922SMathieu Fehr   };
4158ac8c922SMathieu Fehr 
416105c992cSOleksandr "Alex" Zinenko   auto verifier = createVerifier(op, types, attrs);
417105c992cSOleksandr "Alex" Zinenko   if (!verifier)
418105c992cSOleksandr "Alex" Zinenko     return WalkResult::interrupt();
4198ac8c922SMathieu Fehr 
420105c992cSOleksandr "Alex" Zinenko   // IRDL supports only checking number of blocks and argument constraints
421d4bde696SDaniil Dudkin   // It is done in the main verifier to reuse `ConstraintVerifier` context
422d4bde696SDaniil Dudkin   auto regionVerifier = [](Operation *op) { return LogicalResult::success(); };
4238ac8c922SMathieu Fehr 
4248ac8c922SMathieu Fehr   auto opDef = DynamicOpDefinition::get(
4258ac8c922SMathieu Fehr       op.getName(), dialect, std::move(verifier), std::move(regionVerifier),
4268ac8c922SMathieu Fehr       std::move(parser), std::move(printer));
4278ac8c922SMathieu Fehr   dialect->registerDynamicOp(std::move(opDef));
4288ac8c922SMathieu Fehr 
4298ac8c922SMathieu Fehr   return WalkResult::advance();
4308ac8c922SMathieu Fehr }
4318ac8c922SMathieu Fehr 
432c8a581c3SMathieu Fehr /// Get the verifier of a type or attribute definition.
433c8a581c3SMathieu Fehr /// Return nullptr if the definition is invalid.
getAttrOrTypeVerifier(Operation * attrOrTypeDef,ExtensibleDialect * dialect,DenseMap<TypeOp,std::unique_ptr<DynamicTypeDefinition>> & types,DenseMap<AttributeOp,std::unique_ptr<DynamicAttrDefinition>> & attrs)434c8a581c3SMathieu Fehr static DynamicAttrDefinition::VerifierFn getAttrOrTypeVerifier(
435c8a581c3SMathieu Fehr     Operation *attrOrTypeDef, ExtensibleDialect *dialect,
436c8a581c3SMathieu Fehr     DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types,
437c8a581c3SMathieu Fehr     DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>> &attrs) {
438c8a581c3SMathieu Fehr   assert((isa<AttributeOp>(attrOrTypeDef) || isa<TypeOp>(attrOrTypeDef)) &&
439c8a581c3SMathieu Fehr          "Expected an attribute or type definition");
440c8a581c3SMathieu Fehr 
441c8a581c3SMathieu Fehr   // Resolve SSA values to verifier constraint slots
442c8a581c3SMathieu Fehr   SmallVector<Value> constrToValue;
443c8a581c3SMathieu Fehr   for (Operation &op : attrOrTypeDef->getRegion(0).getOps()) {
444c8a581c3SMathieu Fehr     if (isa<VerifyConstraintInterface>(op)) {
445c8a581c3SMathieu Fehr       assert(op.getNumResults() == 1 &&
446c8a581c3SMathieu Fehr              "IRDL constraint operations must have exactly one result");
447c8a581c3SMathieu Fehr       constrToValue.push_back(op.getResult(0));
448c8a581c3SMathieu Fehr     }
449c8a581c3SMathieu Fehr   }
450c8a581c3SMathieu Fehr 
451c8a581c3SMathieu Fehr   // Build the verifiers for each constraint slot
452c8a581c3SMathieu Fehr   SmallVector<std::unique_ptr<Constraint>> constraints;
453c8a581c3SMathieu Fehr   for (Value v : constrToValue) {
454c8a581c3SMathieu Fehr     VerifyConstraintInterface op =
455c8a581c3SMathieu Fehr         cast<VerifyConstraintInterface>(v.getDefiningOp());
456c8a581c3SMathieu Fehr     std::unique_ptr<Constraint> verifier =
457c8a581c3SMathieu Fehr         op.getVerifier(constrToValue, types, attrs);
458c8a581c3SMathieu Fehr     if (!verifier)
459c8a581c3SMathieu Fehr       return {};
460c8a581c3SMathieu Fehr     constraints.push_back(std::move(verifier));
461c8a581c3SMathieu Fehr   }
462c8a581c3SMathieu Fehr 
463c8a581c3SMathieu Fehr   // Get the parameter definitions.
464c8a581c3SMathieu Fehr   std::optional<ParametersOp> params;
465c8a581c3SMathieu Fehr   if (auto attr = dyn_cast<AttributeOp>(attrOrTypeDef))
466c8a581c3SMathieu Fehr     params = attr.getOp<ParametersOp>();
467c8a581c3SMathieu Fehr   else if (auto type = dyn_cast<TypeOp>(attrOrTypeDef))
468c8a581c3SMathieu Fehr     params = type.getOp<ParametersOp>();
469c8a581c3SMathieu Fehr 
470c8a581c3SMathieu Fehr   // Gather which constraint slots correspond to parameter constraints
471c8a581c3SMathieu Fehr   SmallVector<size_t> paramConstraints;
472c8a581c3SMathieu Fehr   if (params.has_value()) {
473c8a581c3SMathieu Fehr     paramConstraints.reserve(params->getArgs().size());
474c8a581c3SMathieu Fehr     for (Value param : params->getArgs()) {
475c8a581c3SMathieu Fehr       for (auto [i, constr] : enumerate(constrToValue)) {
476c8a581c3SMathieu Fehr         if (constr == param) {
477c8a581c3SMathieu Fehr           paramConstraints.push_back(i);
478c8a581c3SMathieu Fehr           break;
479c8a581c3SMathieu Fehr         }
480c8a581c3SMathieu Fehr       }
481c8a581c3SMathieu Fehr     }
482c8a581c3SMathieu Fehr   }
483c8a581c3SMathieu Fehr 
484c8a581c3SMathieu Fehr   auto verifier = [paramConstraints{std::move(paramConstraints)},
485c8a581c3SMathieu Fehr                    constraints{std::move(constraints)}](
486c8a581c3SMathieu Fehr                       function_ref<InFlightDiagnostic()> emitError,
487c8a581c3SMathieu Fehr                       ArrayRef<Attribute> params) {
488c8a581c3SMathieu Fehr     return irdlAttrOrTypeVerifier(emitError, params, constraints,
489c8a581c3SMathieu Fehr                                   paramConstraints);
490c8a581c3SMathieu Fehr   };
491c8a581c3SMathieu Fehr 
492c8a581c3SMathieu Fehr   // While the `std::move` is not required, not adding it triggers a bug in
493c8a581c3SMathieu Fehr   // clang-10.
494c8a581c3SMathieu Fehr   return std::move(verifier);
495c8a581c3SMathieu Fehr }
496c8a581c3SMathieu Fehr 
49742987dfaSMathieu Fehr /// Get the possible bases of a constraint. Return `true` if all bases can
49842987dfaSMathieu Fehr /// potentially be matched.
49942987dfaSMathieu Fehr /// A base is a type or an attribute definition. For instance, the base of
50042987dfaSMathieu Fehr /// `irdl.parametric "!builtin.complex"(...)` is `builtin.complex`.
50142987dfaSMathieu Fehr /// This function returns the following information through arguments:
50242987dfaSMathieu Fehr /// - `paramIds`: the set of type or attribute IDs that are used as bases.
50342987dfaSMathieu Fehr /// - `paramIrdlOps`: the set of IRDL operations that are used as bases.
50442987dfaSMathieu Fehr /// - `isIds`: the set of type or attribute IDs that are used in `irdl.is`
50542987dfaSMathieu Fehr ///   constraints.
getBases(Operation * op,SmallPtrSet<TypeID,4> & paramIds,SmallPtrSet<Operation *,4> & paramIrdlOps,SmallPtrSet<TypeID,4> & isIds)50642987dfaSMathieu Fehr static bool getBases(Operation *op, SmallPtrSet<TypeID, 4> &paramIds,
50742987dfaSMathieu Fehr                      SmallPtrSet<Operation *, 4> &paramIrdlOps,
50842987dfaSMathieu Fehr                      SmallPtrSet<TypeID, 4> &isIds) {
50942987dfaSMathieu Fehr   // For `irdl.any_of`, we get the bases from all its arguments.
5104b526657SMathieu Fehr   if (auto anyOf = dyn_cast<AnyOfOp>(op)) {
51104e2a5d9SMehdi Amini     bool hasAny = false;
51242987dfaSMathieu Fehr     for (Value arg : anyOf.getArgs())
51304e2a5d9SMehdi Amini       hasAny &= getBases(arg.getDefiningOp(), paramIds, paramIrdlOps, isIds);
51404e2a5d9SMehdi Amini     return hasAny;
51542987dfaSMathieu Fehr   }
51642987dfaSMathieu Fehr 
51742987dfaSMathieu Fehr   // For `irdl.all_of`, we get the bases from the first argument.
51842987dfaSMathieu Fehr   // This is restrictive, but we can relax it later if needed.
5194b526657SMathieu Fehr   if (auto allOf = dyn_cast<AllOfOp>(op))
52042987dfaSMathieu Fehr     return getBases(allOf.getArgs()[0].getDefiningOp(), paramIds, paramIrdlOps,
52142987dfaSMathieu Fehr                     isIds);
52242987dfaSMathieu Fehr 
52342987dfaSMathieu Fehr   // For `irdl.parametric`, we get directly the base from the operation.
5244b526657SMathieu Fehr   if (auto params = dyn_cast<ParametricOp>(op)) {
52542987dfaSMathieu Fehr     SymbolRefAttr symRef = params.getBaseType();
526*b86a9c5bSThéo Degioanni     Operation *defOp = irdl::lookupSymbolNearDialect(op, symRef);
52742987dfaSMathieu Fehr     assert(defOp && "symbol reference should refer to an existing operation");
52842987dfaSMathieu Fehr     paramIrdlOps.insert(defOp);
52942987dfaSMathieu Fehr     return false;
53042987dfaSMathieu Fehr   }
53142987dfaSMathieu Fehr 
53242987dfaSMathieu Fehr   // For `irdl.is`, we get the base TypeID directly.
5334b526657SMathieu Fehr   if (auto is = dyn_cast<IsOp>(op)) {
53442987dfaSMathieu Fehr     Attribute expected = is.getExpected();
53542987dfaSMathieu Fehr     isIds.insert(expected.getTypeID());
53642987dfaSMathieu Fehr     return false;
53742987dfaSMathieu Fehr   }
53842987dfaSMathieu Fehr 
53942987dfaSMathieu Fehr   // For `irdl.any`, we return `false` since we can match any type or attribute
54042987dfaSMathieu Fehr   // base.
5414b526657SMathieu Fehr   if (auto isA = dyn_cast<AnyOp>(op))
54242987dfaSMathieu Fehr     return true;
54342987dfaSMathieu Fehr 
54442987dfaSMathieu Fehr   llvm_unreachable("unknown IRDL constraint");
54542987dfaSMathieu Fehr }
54642987dfaSMathieu Fehr 
54742987dfaSMathieu Fehr /// Check that an any_of is in the subset IRDL can handle.
54842987dfaSMathieu Fehr /// IRDL uses a greedy algorithm to match constraints. This means that if we
54942987dfaSMathieu Fehr /// encounter an `any_of` with multiple constraints, we will match the first
55042987dfaSMathieu Fehr /// constraint that is satisfied. Thus, the order of constraints matter in
55142987dfaSMathieu Fehr /// `any_of` with our current algorithm.
55242987dfaSMathieu Fehr /// In order to make the order of constraints irrelevant, we require that
55342987dfaSMathieu Fehr /// all `any_of` constraint parameters are disjoint. For this, we check that
55442987dfaSMathieu Fehr /// the base parameters are all disjoints between `parametric` operations, and
55542987dfaSMathieu Fehr /// that they are disjoint between `parametric` and `is` operations.
55642987dfaSMathieu Fehr /// This restriction will be relaxed in the future, when we will change our
55742987dfaSMathieu Fehr /// algorithm to be non-greedy.
checkCorrectAnyOf(AnyOfOp anyOf)5584b526657SMathieu Fehr static LogicalResult checkCorrectAnyOf(AnyOfOp anyOf) {
55942987dfaSMathieu Fehr   SmallPtrSet<TypeID, 4> paramIds;
56042987dfaSMathieu Fehr   SmallPtrSet<Operation *, 4> paramIrdlOps;
56142987dfaSMathieu Fehr   SmallPtrSet<TypeID, 4> isIds;
56242987dfaSMathieu Fehr 
56342987dfaSMathieu Fehr   for (Value arg : anyOf.getArgs()) {
56442987dfaSMathieu Fehr     Operation *argOp = arg.getDefiningOp();
56542987dfaSMathieu Fehr     SmallPtrSet<TypeID, 4> argParamIds;
56642987dfaSMathieu Fehr     SmallPtrSet<Operation *, 4> argParamIrdlOps;
56742987dfaSMathieu Fehr     SmallPtrSet<TypeID, 4> argIsIds;
56842987dfaSMathieu Fehr 
56942987dfaSMathieu Fehr     // Get the bases of this argument. If it can match any type or attribute,
57042987dfaSMathieu Fehr     // then our `any_of` should not be allowed.
57142987dfaSMathieu Fehr     if (getBases(argOp, argParamIds, argParamIrdlOps, argIsIds))
57242987dfaSMathieu Fehr       return failure();
57342987dfaSMathieu Fehr 
57442987dfaSMathieu Fehr     // We check that the base parameters are all disjoints between `parametric`
57542987dfaSMathieu Fehr     // operations, and that they are disjoint between `parametric` and `is`
57642987dfaSMathieu Fehr     // operations.
57742987dfaSMathieu Fehr     for (TypeID id : argParamIds) {
57842987dfaSMathieu Fehr       if (isIds.count(id))
57942987dfaSMathieu Fehr         return failure();
58042987dfaSMathieu Fehr       bool inserted = paramIds.insert(id).second;
58142987dfaSMathieu Fehr       if (!inserted)
58242987dfaSMathieu Fehr         return failure();
58342987dfaSMathieu Fehr     }
58442987dfaSMathieu Fehr 
58542987dfaSMathieu Fehr     // We check that the base parameters are all disjoints with `irdl.is`
58642987dfaSMathieu Fehr     // operations.
58742987dfaSMathieu Fehr     for (TypeID id : isIds) {
58842987dfaSMathieu Fehr       if (paramIds.count(id))
58942987dfaSMathieu Fehr         return failure();
59042987dfaSMathieu Fehr       isIds.insert(id);
59142987dfaSMathieu Fehr     }
59242987dfaSMathieu Fehr 
59342987dfaSMathieu Fehr     // We check that all `parametric` operations are disjoint. We do not
59442987dfaSMathieu Fehr     // need to check that they are disjoint with `is` operations, since
59542987dfaSMathieu Fehr     // `is` operations cannot refer to attributes defined with `irdl.parametric`
59642987dfaSMathieu Fehr     // operations.
59742987dfaSMathieu Fehr     for (Operation *op : argParamIrdlOps) {
59842987dfaSMathieu Fehr       bool inserted = paramIrdlOps.insert(op).second;
59942987dfaSMathieu Fehr       if (!inserted)
60042987dfaSMathieu Fehr         return failure();
60142987dfaSMathieu Fehr     }
60242987dfaSMathieu Fehr   }
60342987dfaSMathieu Fehr 
60442987dfaSMathieu Fehr   return success();
60542987dfaSMathieu Fehr }
60642987dfaSMathieu Fehr 
6078ac8c922SMathieu Fehr /// Load all dialects in the given module, without loading any operation, type
6088ac8c922SMathieu Fehr /// or attribute definitions.
loadEmptyDialects(ModuleOp op)6098ac8c922SMathieu Fehr static DenseMap<DialectOp, ExtensibleDialect *> loadEmptyDialects(ModuleOp op) {
6108ac8c922SMathieu Fehr   DenseMap<DialectOp, ExtensibleDialect *> dialects;
6118ac8c922SMathieu Fehr   op.walk([&](DialectOp dialectOp) {
6128ac8c922SMathieu Fehr     MLIRContext *ctx = dialectOp.getContext();
6138ac8c922SMathieu Fehr     StringRef dialectName = dialectOp.getName();
6148ac8c922SMathieu Fehr 
6158ac8c922SMathieu Fehr     DynamicDialect *dialect = ctx->getOrLoadDynamicDialect(
6168ac8c922SMathieu Fehr         dialectName, [](DynamicDialect *dialect) {});
6178ac8c922SMathieu Fehr 
6188ac8c922SMathieu Fehr     dialects.insert({dialectOp, dialect});
6198ac8c922SMathieu Fehr   });
6208ac8c922SMathieu Fehr   return dialects;
6218ac8c922SMathieu Fehr }
6228ac8c922SMathieu Fehr 
6238ac8c922SMathieu Fehr /// Preallocate type definitions objects with empty verifiers.
6248ac8c922SMathieu Fehr /// This in particular allocates a TypeID for each type definition.
6258ac8c922SMathieu Fehr static DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>>
preallocateTypeDefs(ModuleOp op,DenseMap<DialectOp,ExtensibleDialect * > dialects)6268ac8c922SMathieu Fehr preallocateTypeDefs(ModuleOp op,
6278ac8c922SMathieu Fehr                     DenseMap<DialectOp, ExtensibleDialect *> dialects) {
6288ac8c922SMathieu Fehr   DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> typeDefs;
6298ac8c922SMathieu Fehr   op.walk([&](TypeOp typeOp) {
6308ac8c922SMathieu Fehr     ExtensibleDialect *dialect = dialects[typeOp.getParentOp()];
6318ac8c922SMathieu Fehr     auto typeDef = DynamicTypeDefinition::get(
6328ac8c922SMathieu Fehr         typeOp.getName(), dialect,
6338ac8c922SMathieu Fehr         [](function_ref<InFlightDiagnostic()>, ArrayRef<Attribute>) {
6348ac8c922SMathieu Fehr           return success();
6358ac8c922SMathieu Fehr         });
6368ac8c922SMathieu Fehr     typeDefs.try_emplace(typeOp, std::move(typeDef));
6378ac8c922SMathieu Fehr   });
6388ac8c922SMathieu Fehr   return typeDefs;
6398ac8c922SMathieu Fehr }
6408ac8c922SMathieu Fehr 
6418ac8c922SMathieu Fehr /// Preallocate attribute definitions objects with empty verifiers.
6428ac8c922SMathieu Fehr /// This in particular allocates a TypeID for each attribute definition.
6438ac8c922SMathieu Fehr static DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>>
preallocateAttrDefs(ModuleOp op,DenseMap<DialectOp,ExtensibleDialect * > dialects)6448ac8c922SMathieu Fehr preallocateAttrDefs(ModuleOp op,
6458ac8c922SMathieu Fehr                     DenseMap<DialectOp, ExtensibleDialect *> dialects) {
6468ac8c922SMathieu Fehr   DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>> attrDefs;
6478ac8c922SMathieu Fehr   op.walk([&](AttributeOp attrOp) {
6488ac8c922SMathieu Fehr     ExtensibleDialect *dialect = dialects[attrOp.getParentOp()];
6498ac8c922SMathieu Fehr     auto attrDef = DynamicAttrDefinition::get(
6508ac8c922SMathieu Fehr         attrOp.getName(), dialect,
6518ac8c922SMathieu Fehr         [](function_ref<InFlightDiagnostic()>, ArrayRef<Attribute>) {
6528ac8c922SMathieu Fehr           return success();
6538ac8c922SMathieu Fehr         });
6548ac8c922SMathieu Fehr     attrDefs.try_emplace(attrOp, std::move(attrDef));
6558ac8c922SMathieu Fehr   });
6568ac8c922SMathieu Fehr   return attrDefs;
6578ac8c922SMathieu Fehr }
6588ac8c922SMathieu Fehr 
loadDialects(ModuleOp op)6598ac8c922SMathieu Fehr LogicalResult mlir::irdl::loadDialects(ModuleOp op) {
66042987dfaSMathieu Fehr   // First, check that all any_of constraints are in a correct form.
66142987dfaSMathieu Fehr   // This is to ensure we can do the verification correctly.
6624b526657SMathieu Fehr   WalkResult anyOfCorrects = op.walk(
6634b526657SMathieu Fehr       [](AnyOfOp anyOf) { return (WalkResult)checkCorrectAnyOf(anyOf); });
66442987dfaSMathieu Fehr   if (anyOfCorrects.wasInterrupted())
66542987dfaSMathieu Fehr     return op.emitError("any_of constraints are not in the correct form");
66642987dfaSMathieu Fehr 
6678ac8c922SMathieu Fehr   // Preallocate all dialects, and type and attribute definitions.
6688ac8c922SMathieu Fehr   // In particular, this allocates TypeIDs so type and attributes can have
6698ac8c922SMathieu Fehr   // verifiers that refer to each other.
6708ac8c922SMathieu Fehr   DenseMap<DialectOp, ExtensibleDialect *> dialects = loadEmptyDialects(op);
6718ac8c922SMathieu Fehr   DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> types =
6728ac8c922SMathieu Fehr       preallocateTypeDefs(op, dialects);
6738ac8c922SMathieu Fehr   DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>> attrs =
6748ac8c922SMathieu Fehr       preallocateAttrDefs(op, dialects);
6758ac8c922SMathieu Fehr 
676c8a581c3SMathieu Fehr   // Set the verifier for types.
677c8a581c3SMathieu Fehr   WalkResult res = op.walk([&](TypeOp typeOp) {
678c8a581c3SMathieu Fehr     DynamicAttrDefinition::VerifierFn verifier = getAttrOrTypeVerifier(
679c8a581c3SMathieu Fehr         typeOp, dialects[typeOp.getParentOp()], types, attrs);
680c8a581c3SMathieu Fehr     if (!verifier)
681c8a581c3SMathieu Fehr       return WalkResult::interrupt();
682c8a581c3SMathieu Fehr     types[typeOp]->setVerifyFn(std::move(verifier));
683c8a581c3SMathieu Fehr     return WalkResult::advance();
684c8a581c3SMathieu Fehr   });
685c8a581c3SMathieu Fehr   if (res.wasInterrupted())
686c8a581c3SMathieu Fehr     return failure();
687c8a581c3SMathieu Fehr 
688c8a581c3SMathieu Fehr   // Set the verifier for attributes.
689c8a581c3SMathieu Fehr   res = op.walk([&](AttributeOp attrOp) {
690c8a581c3SMathieu Fehr     DynamicAttrDefinition::VerifierFn verifier = getAttrOrTypeVerifier(
691c8a581c3SMathieu Fehr         attrOp, dialects[attrOp.getParentOp()], types, attrs);
692c8a581c3SMathieu Fehr     if (!verifier)
693c8a581c3SMathieu Fehr       return WalkResult::interrupt();
694c8a581c3SMathieu Fehr     attrs[attrOp]->setVerifyFn(std::move(verifier));
695c8a581c3SMathieu Fehr     return WalkResult::advance();
696c8a581c3SMathieu Fehr   });
697c8a581c3SMathieu Fehr   if (res.wasInterrupted())
698c8a581c3SMathieu Fehr     return failure();
699c8a581c3SMathieu Fehr 
7008ac8c922SMathieu Fehr   // Define and load all operations.
701c8a581c3SMathieu Fehr   res = op.walk([&](OperationOp opOp) {
702c8a581c3SMathieu Fehr     return loadOperation(opOp, dialects[opOp.getParentOp()], types, attrs);
7038ac8c922SMathieu Fehr   });
7048ac8c922SMathieu Fehr   if (res.wasInterrupted())
7058ac8c922SMathieu Fehr     return failure();
7068ac8c922SMathieu Fehr 
7078ac8c922SMathieu Fehr   // Load all types in their dialects.
7088ac8c922SMathieu Fehr   for (auto &pair : types) {
7098ac8c922SMathieu Fehr     ExtensibleDialect *dialect = dialects[pair.first.getParentOp()];
7108ac8c922SMathieu Fehr     dialect->registerDynamicType(std::move(pair.second));
7118ac8c922SMathieu Fehr   }
7128ac8c922SMathieu Fehr 
7138ac8c922SMathieu Fehr   // Load all attributes in their dialects.
7148ac8c922SMathieu Fehr   for (auto &pair : attrs) {
7158ac8c922SMathieu Fehr     ExtensibleDialect *dialect = dialects[pair.first.getParentOp()];
7168ac8c922SMathieu Fehr     dialect->registerDynamicAttr(std::move(pair.second));
7178ac8c922SMathieu Fehr   }
7188ac8c922SMathieu Fehr 
7198ac8c922SMathieu Fehr   return success();
7208ac8c922SMathieu Fehr }
721