brintos

brintos / llvm-project-archived public Read only

0
0
Text · 27.8 KiB · 8d10aac Raw
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> &paramIds,504                     SmallPtrSet<Operation *, 4> &paramIrdlOps,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