718 lines · cpp
1//===- IRDLLoading.cpp - IRDL dialect loading --------------------- C++ -*-===//2//3// This file is licensed under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Manages the loading of MLIR objects from IRDL operations.10//11//===----------------------------------------------------------------------===//12 13#include "mlir/Dialect/IRDL/IRDLLoading.h"14#include "mlir/Dialect/IRDL/IR/IRDL.h"15#include "mlir/Dialect/IRDL/IR/IRDLInterfaces.h"16#include "mlir/Dialect/IRDL/IRDLSymbols.h"17#include "mlir/Dialect/IRDL/IRDLVerifiers.h"18#include "mlir/IR/Attributes.h"19#include "mlir/IR/BuiltinOps.h"20#include "mlir/IR/ExtensibleDialect.h"21#include "mlir/IR/OperationSupport.h"22#include "llvm/ADT/STLExtras.h"23 24using namespace mlir;25using namespace mlir::irdl;26 27/// Verify that the given list of parameters satisfy the given constraints.28/// This encodes the logic of the verification method for attributes and types29/// defined with IRDL.30static LogicalResult31irdlAttrOrTypeVerifier(function_ref<InFlightDiagnostic()> emitError,32 ArrayRef<Attribute> params,33 ArrayRef<std::unique_ptr<Constraint>> constraints,34 ArrayRef<size_t> paramConstraints) {35 if (params.size() != paramConstraints.size()) {36 emitError() << "expected " << paramConstraints.size()37 << " type arguments, but had " << params.size();38 return failure();39 }40 41 ConstraintVerifier verifier(constraints);42 43 // Check that each parameter satisfies its constraint.44 for (auto [i, param] : enumerate(params))45 if (failed(verifier.verify(emitError, param, paramConstraints[i])))46 return failure();47 48 return success();49}50 51/// Get the operand segment sizes from the attribute dictionary.52LogicalResult getSegmentSizesFromAttr(Operation *op, StringRef elemName,53 StringRef attrName, unsigned numElements,54 ArrayRef<Variadicity> variadicities,55 SmallVectorImpl<int> &segmentSizes) {56 // Get the segment sizes attribute, and check that it is of the right type.57 Attribute segmentSizesAttr = op->getAttr(attrName);58 if (!segmentSizesAttr) {59 return op->emitError() << "'" << attrName60 << "' attribute is expected but not provided";61 }62 63 auto denseSegmentSizes = dyn_cast<DenseI32ArrayAttr>(segmentSizesAttr);64 if (!denseSegmentSizes) {65 return op->emitError() << "'" << attrName66 << "' attribute is expected to be a dense i32 array";67 }68 69 if (denseSegmentSizes.size() != (int64_t)variadicities.size()) {70 return op->emitError() << "'" << attrName << "' attribute for specifying "71 << elemName << " segments must have "72 << variadicities.size() << " elements, but got "73 << denseSegmentSizes.size();74 }75 76 // Check that the segment sizes are corresponding to the given variadicities,77 for (auto [i, segmentSize, variadicity] :78 enumerate(denseSegmentSizes.asArrayRef(), variadicities)) {79 if (segmentSize < 0)80 return op->emitError()81 << "'" << attrName << "' attribute for specifying " << elemName82 << " segments must have non-negative values";83 if (variadicity == Variadicity::single && segmentSize != 1)84 return op->emitError() << "element " << i << " in '" << attrName85 << "' attribute must be equal to 1";86 87 if (variadicity == Variadicity::optional && segmentSize > 1)88 return op->emitError() << "element " << i << " in '" << attrName89 << "' attribute must be equal to 0 or 1";90 91 segmentSizes.push_back(segmentSize);92 }93 94 // Check that the sum of the segment sizes is equal to the number of elements.95 int32_t sum = 0;96 for (int32_t segmentSize : denseSegmentSizes.asArrayRef())97 sum += segmentSize;98 if (sum != static_cast<int32_t>(numElements))99 return op->emitError() << "sum of elements in '" << attrName100 << "' attribute must be equal to the number of "101 << elemName << "s";102 103 return success();104}105 106/// Compute the segment sizes of the given element (operands, results).107/// If the operation has more than two non-single elements (optional or108/// variadic), then get the segment sizes from the attribute dictionary.109/// Otherwise, compute the segment sizes from the number of elements.110/// `elemName` should be either `"operand"` or `"result"`.111LogicalResult getSegmentSizes(Operation *op, StringRef elemName,112 StringRef attrName, unsigned numElements,113 ArrayRef<Variadicity> variadicities,114 SmallVectorImpl<int> &segmentSizes) {115 // If we have more than one non-single variadicity, we need to get the116 // segment sizes from the attribute dictionary.117 int numberNonSingle = count_if(118 variadicities, [](Variadicity v) { return v != Variadicity::single; });119 if (numberNonSingle > 1)120 return getSegmentSizesFromAttr(op, elemName, attrName, numElements,121 variadicities, segmentSizes);122 123 // If we only have single variadicities, the segments sizes are all 1.124 if (numberNonSingle == 0) {125 if (numElements != variadicities.size()) {126 return op->emitError() << "op expects exactly " << variadicities.size()127 << " " << elemName << "s, but got " << numElements;128 }129 for (size_t i = 0, e = variadicities.size(); i < e; ++i)130 segmentSizes.push_back(1);131 return success();132 }133 134 assert(numberNonSingle == 1);135 136 // There is exactly one non-single element, so we can137 // compute its size and check that it is valid.138 int nonSingleSegmentSize = static_cast<int>(numElements) -139 static_cast<int>(variadicities.size()) + 1;140 141 if (nonSingleSegmentSize < 0) {142 return op->emitError() << "op expects at least " << variadicities.size() - 1143 << " " << elemName << "s, but got " << numElements;144 }145 146 // Add the segment sizes.147 for (Variadicity variadicity : variadicities) {148 if (variadicity == Variadicity::single) {149 segmentSizes.push_back(1);150 continue;151 }152 153 // If we have an optional element, we should check that it represents154 // zero or one elements.155 if (nonSingleSegmentSize > 1 && variadicity == Variadicity::optional)156 return op->emitError() << "op expects at most " << variadicities.size()157 << " " << elemName << "s, but got " << numElements;158 159 segmentSizes.push_back(nonSingleSegmentSize);160 }161 162 return success();163}164 165/// Compute the segment sizes of the given operands.166/// If the operation has more than two non-single operands (optional or167/// variadic), then get the segment sizes from the attribute dictionary.168/// Otherwise, compute the segment sizes from the number of operands.169LogicalResult getOperandSegmentSizes(Operation *op,170 ArrayRef<Variadicity> variadicities,171 SmallVectorImpl<int> &segmentSizes) {172 return getSegmentSizes(op, "operand", "operandSegmentSizes",173 op->getNumOperands(), variadicities, segmentSizes);174}175 176/// Compute the segment sizes of the given results.177/// If the operation has more than two non-single results (optional or178/// variadic), then get the segment sizes from the attribute dictionary.179/// Otherwise, compute the segment sizes from the number of results.180LogicalResult getResultSegmentSizes(Operation *op,181 ArrayRef<Variadicity> variadicities,182 SmallVectorImpl<int> &segmentSizes) {183 return getSegmentSizes(op, "result", "resultSegmentSizes",184 op->getNumResults(), variadicities, segmentSizes);185}186 187/// Verify that the given operation satisfies the given constraints.188/// This encodes the logic of the verification method for operations defined189/// with IRDL.190static LogicalResult irdlOpVerifier(191 Operation *op, ConstraintVerifier &verifier,192 ArrayRef<size_t> operandConstrs, ArrayRef<Variadicity> operandVariadicity,193 ArrayRef<size_t> resultConstrs, ArrayRef<Variadicity> resultVariadicity,194 const DenseMap<StringAttr, size_t> &attributeConstrs) {195 // Get the segment sizes for the operands.196 // This will check that the number of operands is correct.197 SmallVector<int> operandSegmentSizes;198 if (failed(199 getOperandSegmentSizes(op, operandVariadicity, operandSegmentSizes)))200 return failure();201 202 // Get the segment sizes for the results.203 // This will check that the number of results is correct.204 SmallVector<int> resultSegmentSizes;205 if (failed(getResultSegmentSizes(op, resultVariadicity, resultSegmentSizes)))206 return failure();207 208 auto emitError = [op] { return op->emitError(); };209 210 /// Сheck that we have all needed attributes passed211 /// and they satisfy the constraints.212 DictionaryAttr actualAttrs = op->getAttrDictionary();213 214 for (auto [name, constraint] : attributeConstrs) {215 /// First, check if the attribute actually passed.216 std::optional<NamedAttribute> actual = actualAttrs.getNamed(name);217 if (!actual.has_value())218 return op->emitOpError()219 << "attribute " << name << " is expected but not provided";220 221 /// Then, check if the attribute value satisfies the constraint.222 if (failed(verifier.verify({emitError}, actual->getValue(), constraint)))223 return failure();224 }225 226 // Check that all operands satisfy the constraints227 int operandIdx = 0;228 for (auto [defIndex, segmentSize] : enumerate(operandSegmentSizes)) {229 for (int i = 0; i < segmentSize; i++) {230 if (failed(verifier.verify(231 {emitError}, TypeAttr::get(op->getOperandTypes()[operandIdx]),232 operandConstrs[defIndex])))233 return failure();234 ++operandIdx;235 }236 }237 238 // Check that all results satisfy the constraints239 int resultIdx = 0;240 for (auto [defIndex, segmentSize] : enumerate(resultSegmentSizes)) {241 for (int i = 0; i < segmentSize; i++) {242 if (failed(verifier.verify({emitError},243 TypeAttr::get(op->getResultTypes()[resultIdx]),244 resultConstrs[defIndex])))245 return failure();246 ++resultIdx;247 }248 }249 250 return success();251}252 253static LogicalResult irdlRegionVerifier(254 Operation *op, ConstraintVerifier &verifier,255 ArrayRef<std::unique_ptr<RegionConstraint>> regionsConstraints) {256 if (op->getNumRegions() != regionsConstraints.size()) {257 return op->emitOpError()258 << "unexpected number of regions: expected "259 << regionsConstraints.size() << " but got " << op->getNumRegions();260 }261 262 for (auto [constraint, region] :263 llvm::zip(regionsConstraints, op->getRegions()))264 if (failed(constraint->verify(region, verifier)))265 return failure();266 267 return success();268}269 270llvm::unique_function<LogicalResult(Operation *) const>271mlir::irdl::createVerifier(272 OperationOp op,273 const DenseMap<irdl::TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types,274 const DenseMap<irdl::AttributeOp, std::unique_ptr<DynamicAttrDefinition>>275 &attrs) {276 // Resolve SSA values to verifier constraint slots277 SmallVector<Value> constrToValue;278 SmallVector<Value> regionToValue;279 for (Operation &op : op->getRegion(0).getOps()) {280 if (isa<VerifyConstraintInterface>(op)) {281 if (op.getNumResults() != 1) {282 op.emitError()283 << "IRDL constraint operations must have exactly one result";284 return nullptr;285 }286 constrToValue.push_back(op.getResult(0));287 }288 if (isa<VerifyRegionInterface>(op)) {289 if (op.getNumResults() != 1) {290 op.emitError()291 << "IRDL constraint operations must have exactly one result";292 return nullptr;293 }294 regionToValue.push_back(op.getResult(0));295 }296 }297 298 // Build the verifiers for each constraint slot299 SmallVector<std::unique_ptr<Constraint>> constraints;300 for (Value v : constrToValue) {301 VerifyConstraintInterface op =302 cast<VerifyConstraintInterface>(v.getDefiningOp());303 std::unique_ptr<Constraint> verifier =304 op.getVerifier(constrToValue, types, attrs);305 if (!verifier)306 return nullptr;307 constraints.push_back(std::move(verifier));308 }309 310 // Build region constraints311 SmallVector<std::unique_ptr<RegionConstraint>> regionConstraints;312 for (Value v : regionToValue) {313 VerifyRegionInterface op = cast<VerifyRegionInterface>(v.getDefiningOp());314 std::unique_ptr<RegionConstraint> verifier =315 op.getVerifier(constrToValue, types, attrs);316 regionConstraints.push_back(std::move(verifier));317 }318 319 SmallVector<size_t> operandConstraints;320 SmallVector<Variadicity> operandVariadicity;321 322 // Gather which constraint slots correspond to operand constraints323 auto operandsOp = op.getOp<OperandsOp>();324 if (operandsOp.has_value()) {325 operandConstraints.reserve(operandsOp->getArgs().size());326 for (Value operand : operandsOp->getArgs()) {327 for (auto [i, constr] : enumerate(constrToValue)) {328 if (constr == operand) {329 operandConstraints.push_back(i);330 break;331 }332 }333 }334 335 // Gather the variadicities of each operand336 for (VariadicityAttr attr : operandsOp->getVariadicity())337 operandVariadicity.push_back(attr.getValue());338 }339 340 SmallVector<size_t> resultConstraints;341 SmallVector<Variadicity> resultVariadicity;342 343 // Gather which constraint slots correspond to result constraints344 auto resultsOp = op.getOp<ResultsOp>();345 if (resultsOp.has_value()) {346 resultConstraints.reserve(resultsOp->getArgs().size());347 for (Value result : resultsOp->getArgs()) {348 for (auto [i, constr] : enumerate(constrToValue)) {349 if (constr == result) {350 resultConstraints.push_back(i);351 break;352 }353 }354 }355 356 // Gather the variadicities of each result357 for (Attribute attr : resultsOp->getVariadicity())358 resultVariadicity.push_back(cast<VariadicityAttr>(attr).getValue());359 }360 361 // Gather which constraint slots correspond to attributes constraints362 DenseMap<StringAttr, size_t> attributeConstraints;363 auto attributesOp = op.getOp<AttributesOp>();364 if (attributesOp.has_value()) {365 const Operation::operand_range values = attributesOp->getAttributeValues();366 const ArrayAttr names = attributesOp->getAttributeValueNames();367 368 for (const auto &[name, value] : llvm::zip(names, values)) {369 for (auto [i, constr] : enumerate(constrToValue)) {370 if (constr == value) {371 attributeConstraints[cast<StringAttr>(name)] = i;372 break;373 }374 }375 }376 }377 378 return379 [constraints{std::move(constraints)},380 regionConstraints{std::move(regionConstraints)},381 operandConstraints{std::move(operandConstraints)},382 operandVariadicity{std::move(operandVariadicity)},383 resultConstraints{std::move(resultConstraints)},384 resultVariadicity{std::move(resultVariadicity)},385 attributeConstraints{std::move(attributeConstraints)}](Operation *op) {386 ConstraintVerifier verifier(constraints);387 const LogicalResult opVerifierResult = irdlOpVerifier(388 op, verifier, operandConstraints, operandVariadicity,389 resultConstraints, resultVariadicity, attributeConstraints);390 const LogicalResult opRegionVerifierResult =391 irdlRegionVerifier(op, verifier, regionConstraints);392 return LogicalResult::success(opVerifierResult.succeeded() &&393 opRegionVerifierResult.succeeded());394 };395}396 397/// Define and load an operation represented by a `irdl.operation`398/// operation.399static WalkResult loadOperation(400 OperationOp op, ExtensibleDialect *dialect,401 const DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types,402 const DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>>403 &attrs) {404 405 // IRDL does not support defining custom parsers or printers.406 auto parser = [](OpAsmParser &parser, OperationState &result) {407 return failure();408 };409 auto printer = [](Operation *op, OpAsmPrinter &printer, StringRef) {410 printer.printGenericOp(op);411 };412 413 auto verifier = createVerifier(op, types, attrs);414 if (!verifier)415 return WalkResult::interrupt();416 417 // IRDL supports only checking number of blocks and argument constraints418 // It is done in the main verifier to reuse `ConstraintVerifier` context419 auto regionVerifier = [](Operation *op) { return LogicalResult::success(); };420 421 auto opDef = DynamicOpDefinition::get(422 op.getName(), dialect, std::move(verifier), std::move(regionVerifier),423 std::move(parser), std::move(printer));424 dialect->registerDynamicOp(std::move(opDef));425 426 return WalkResult::advance();427}428 429/// Get the verifier of a type or attribute definition.430/// Return nullptr if the definition is invalid.431static DynamicAttrDefinition::VerifierFn getAttrOrTypeVerifier(432 Operation *attrOrTypeDef, ExtensibleDialect *dialect,433 DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types,434 DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>> &attrs) {435 assert((isa<AttributeOp>(attrOrTypeDef) || isa<TypeOp>(attrOrTypeDef)) &&436 "Expected an attribute or type definition");437 438 // Resolve SSA values to verifier constraint slots439 SmallVector<Value> constrToValue;440 for (Operation &op : attrOrTypeDef->getRegion(0).getOps()) {441 if (isa<VerifyConstraintInterface>(op)) {442 assert(op.getNumResults() == 1 &&443 "IRDL constraint operations must have exactly one result");444 constrToValue.push_back(op.getResult(0));445 }446 }447 448 // Build the verifiers for each constraint slot449 SmallVector<std::unique_ptr<Constraint>> constraints;450 for (Value v : constrToValue) {451 VerifyConstraintInterface op =452 cast<VerifyConstraintInterface>(v.getDefiningOp());453 std::unique_ptr<Constraint> verifier =454 op.getVerifier(constrToValue, types, attrs);455 if (!verifier)456 return {};457 constraints.push_back(std::move(verifier));458 }459 460 // Get the parameter definitions.461 std::optional<ParametersOp> params;462 if (auto attr = dyn_cast<AttributeOp>(attrOrTypeDef))463 params = attr.getOp<ParametersOp>();464 else if (auto type = dyn_cast<TypeOp>(attrOrTypeDef))465 params = type.getOp<ParametersOp>();466 467 // Gather which constraint slots correspond to parameter constraints468 SmallVector<size_t> paramConstraints;469 if (params.has_value()) {470 paramConstraints.reserve(params->getArgs().size());471 for (Value param : params->getArgs()) {472 for (auto [i, constr] : enumerate(constrToValue)) {473 if (constr == param) {474 paramConstraints.push_back(i);475 break;476 }477 }478 }479 }480 481 auto verifier = [paramConstraints{std::move(paramConstraints)},482 constraints{std::move(constraints)}](483 function_ref<InFlightDiagnostic()> emitError,484 ArrayRef<Attribute> params) {485 return irdlAttrOrTypeVerifier(emitError, params, constraints,486 paramConstraints);487 };488 489 // While the `std::move` is not required, not adding it triggers a bug in490 // clang-10.491 return std::move(verifier);492}493 494/// Get the possible bases of a constraint. Return `true` if all bases can495/// potentially be matched.496/// A base is a type or an attribute definition. For instance, the base of497/// `irdl.parametric "!builtin.complex"(...)` is `builtin.complex`.498/// This function returns the following information through arguments:499/// - `paramIds`: the set of type or attribute IDs that are used as bases.500/// - `paramIrdlOps`: the set of IRDL operations that are used as bases.501/// - `isIds`: the set of type or attribute IDs that are used in `irdl.is`502/// constraints.503static bool getBases(Operation *op, SmallPtrSet<TypeID, 4> ¶mIds,504 SmallPtrSet<Operation *, 4> ¶mIrdlOps,505 SmallPtrSet<TypeID, 4> &isIds) {506 // For `irdl.any_of`, we get the bases from all its arguments.507 if (auto anyOf = dyn_cast<AnyOfOp>(op)) {508 bool hasAny = false;509 for (Value arg : anyOf.getArgs())510 hasAny &= getBases(arg.getDefiningOp(), paramIds, paramIrdlOps, isIds);511 return hasAny;512 }513 514 // For `irdl.all_of`, we get the bases from the first argument.515 // This is restrictive, but we can relax it later if needed.516 if (auto allOf = dyn_cast<AllOfOp>(op))517 return getBases(allOf.getArgs()[0].getDefiningOp(), paramIds, paramIrdlOps,518 isIds);519 520 // For `irdl.parametric`, we get directly the base from the operation.521 if (auto params = dyn_cast<ParametricOp>(op)) {522 SymbolRefAttr symRef = params.getBaseType();523 Operation *defOp = irdl::lookupSymbolNearDialect(op, symRef);524 assert(defOp && "symbol reference should refer to an existing operation");525 paramIrdlOps.insert(defOp);526 return false;527 }528 529 // For `irdl.is`, we get the base TypeID directly.530 if (auto is = dyn_cast<IsOp>(op)) {531 Attribute expected = is.getExpected();532 isIds.insert(expected.getTypeID());533 return false;534 }535 536 // For `irdl.any`, we return `false` since we can match any type or attribute537 // base.538 if (auto isA = dyn_cast<AnyOp>(op))539 return true;540 541 llvm_unreachable("unknown IRDL constraint");542}543 544/// Check that an any_of is in the subset IRDL can handle.545/// IRDL uses a greedy algorithm to match constraints. This means that if we546/// encounter an `any_of` with multiple constraints, we will match the first547/// constraint that is satisfied. Thus, the order of constraints matter in548/// `any_of` with our current algorithm.549/// In order to make the order of constraints irrelevant, we require that550/// all `any_of` constraint parameters are disjoint. For this, we check that551/// the base parameters are all disjoints between `parametric` operations, and552/// that they are disjoint between `parametric` and `is` operations.553/// This restriction will be relaxed in the future, when we will change our554/// algorithm to be non-greedy.555static LogicalResult checkCorrectAnyOf(AnyOfOp anyOf) {556 SmallPtrSet<TypeID, 4> paramIds;557 SmallPtrSet<Operation *, 4> paramIrdlOps;558 SmallPtrSet<TypeID, 4> isIds;559 560 for (Value arg : anyOf.getArgs()) {561 Operation *argOp = arg.getDefiningOp();562 SmallPtrSet<TypeID, 4> argParamIds;563 SmallPtrSet<Operation *, 4> argParamIrdlOps;564 SmallPtrSet<TypeID, 4> argIsIds;565 566 // Get the bases of this argument. If it can match any type or attribute,567 // then our `any_of` should not be allowed.568 if (getBases(argOp, argParamIds, argParamIrdlOps, argIsIds))569 return failure();570 571 // We check that the base parameters are all disjoints between `parametric`572 // operations, and that they are disjoint between `parametric` and `is`573 // operations.574 for (TypeID id : argParamIds) {575 if (isIds.count(id))576 return failure();577 bool inserted = paramIds.insert(id).second;578 if (!inserted)579 return failure();580 }581 582 // We check that the base parameters are all disjoints with `irdl.is`583 // operations.584 for (TypeID id : isIds) {585 if (paramIds.count(id))586 return failure();587 isIds.insert(id);588 }589 590 // We check that all `parametric` operations are disjoint. We do not591 // need to check that they are disjoint with `is` operations, since592 // `is` operations cannot refer to attributes defined with `irdl.parametric`593 // operations.594 for (Operation *op : argParamIrdlOps) {595 bool inserted = paramIrdlOps.insert(op).second;596 if (!inserted)597 return failure();598 }599 }600 601 return success();602}603 604/// Load all dialects in the given module, without loading any operation, type605/// or attribute definitions.606static DenseMap<DialectOp, ExtensibleDialect *> loadEmptyDialects(ModuleOp op) {607 DenseMap<DialectOp, ExtensibleDialect *> dialects;608 op.walk([&](DialectOp dialectOp) {609 MLIRContext *ctx = dialectOp.getContext();610 StringRef dialectName = dialectOp.getName();611 612 DynamicDialect *dialect = ctx->getOrLoadDynamicDialect(613 dialectName, [](DynamicDialect *dialect) {});614 615 dialects.insert({dialectOp, dialect});616 });617 return dialects;618}619 620/// Preallocate type definitions objects with empty verifiers.621/// This in particular allocates a TypeID for each type definition.622static DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>>623preallocateTypeDefs(ModuleOp op,624 DenseMap<DialectOp, ExtensibleDialect *> dialects) {625 DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> typeDefs;626 op.walk([&](TypeOp typeOp) {627 ExtensibleDialect *dialect = dialects[typeOp.getParentOp()];628 auto typeDef = DynamicTypeDefinition::get(629 typeOp.getName(), dialect,630 [](function_ref<InFlightDiagnostic()>, ArrayRef<Attribute>) {631 return success();632 });633 typeDefs.try_emplace(typeOp, std::move(typeDef));634 });635 return typeDefs;636}637 638/// Preallocate attribute definitions objects with empty verifiers.639/// This in particular allocates a TypeID for each attribute definition.640static DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>>641preallocateAttrDefs(ModuleOp op,642 DenseMap<DialectOp, ExtensibleDialect *> dialects) {643 DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>> attrDefs;644 op.walk([&](AttributeOp attrOp) {645 ExtensibleDialect *dialect = dialects[attrOp.getParentOp()];646 auto attrDef = DynamicAttrDefinition::get(647 attrOp.getName(), dialect,648 [](function_ref<InFlightDiagnostic()>, ArrayRef<Attribute>) {649 return success();650 });651 attrDefs.try_emplace(attrOp, std::move(attrDef));652 });653 return attrDefs;654}655 656LogicalResult mlir::irdl::loadDialects(ModuleOp op) {657 // First, check that all any_of constraints are in a correct form.658 // This is to ensure we can do the verification correctly.659 WalkResult anyOfCorrects = op.walk(660 [](AnyOfOp anyOf) { return (WalkResult)checkCorrectAnyOf(anyOf); });661 if (anyOfCorrects.wasInterrupted())662 return op.emitError("any_of constraints are not in the correct form");663 664 // Preallocate all dialects, and type and attribute definitions.665 // In particular, this allocates TypeIDs so type and attributes can have666 // verifiers that refer to each other.667 DenseMap<DialectOp, ExtensibleDialect *> dialects = loadEmptyDialects(op);668 DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> types =669 preallocateTypeDefs(op, dialects);670 DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>> attrs =671 preallocateAttrDefs(op, dialects);672 673 // Set the verifier for types.674 WalkResult res = op.walk([&](TypeOp typeOp) {675 DynamicAttrDefinition::VerifierFn verifier = getAttrOrTypeVerifier(676 typeOp, dialects[typeOp.getParentOp()], types, attrs);677 if (!verifier)678 return WalkResult::interrupt();679 types[typeOp]->setVerifyFn(std::move(verifier));680 return WalkResult::advance();681 });682 if (res.wasInterrupted())683 return failure();684 685 // Set the verifier for attributes.686 res = op.walk([&](AttributeOp attrOp) {687 DynamicAttrDefinition::VerifierFn verifier = getAttrOrTypeVerifier(688 attrOp, dialects[attrOp.getParentOp()], types, attrs);689 if (!verifier)690 return WalkResult::interrupt();691 attrs[attrOp]->setVerifyFn(std::move(verifier));692 return WalkResult::advance();693 });694 if (res.wasInterrupted())695 return failure();696 697 // Define and load all operations.698 res = op.walk([&](OperationOp opOp) {699 return loadOperation(opOp, dialects[opOp.getParentOp()], types, attrs);700 });701 if (res.wasInterrupted())702 return failure();703 704 // Load all types in their dialects.705 for (auto &pair : types) {706 ExtensibleDialect *dialect = dialects[pair.first.getParentOp()];707 dialect->registerDynamicType(std::move(pair.second));708 }709 710 // Load all attributes in their dialects.711 for (auto &pair : attrs) {712 ExtensibleDialect *dialect = dialects[pair.first.getParentOp()];713 dialect->registerDynamicAttr(std::move(pair.second));714 }715 716 return success();717}718