22 #include "llvm/ADT/STLExtras.h"
23 #include "llvm/ADT/SmallPtrSet.h"
24 #include "llvm/Support/SMLoc.h"
36 ArrayRef<std::unique_ptr<Constraint>> constraints,
38 if (params.size() != paramConstraints.size()) {
39 emitError() <<
"expected " << paramConstraints.size()
40 <<
" type arguments, but had " << params.size();
56 StringRef attrName,
unsigned numElements,
61 if (!segmentSizesAttr) {
63 <<
"' attribute is expected but not provided";
66 auto denseSegmentSizes = dyn_cast<DenseI32ArrayAttr>(segmentSizesAttr);
67 if (!denseSegmentSizes) {
69 <<
"' attribute is expected to be a dense i32 array";
72 if (denseSegmentSizes.size() != (int64_t)variadicities.size()) {
73 return op->
emitError() <<
"'" << attrName <<
"' attribute for specifying "
74 << elemName <<
" segments must have "
75 << variadicities.size() <<
" elements, but got "
76 << denseSegmentSizes.size();
80 for (
auto [i, segmentSize, variadicity] :
81 enumerate(denseSegmentSizes.asArrayRef(), variadicities)) {
84 <<
"'" << attrName <<
"' attribute for specifying " << elemName
85 <<
" segments must have non-negative values";
86 if (variadicity == Variadicity::single && segmentSize != 1)
87 return op->
emitError() <<
"element " << i <<
" in '" << attrName
88 <<
"' attribute must be equal to 1";
90 if (variadicity == Variadicity::optional && segmentSize > 1)
91 return op->
emitError() <<
"element " << i <<
" in '" << attrName
92 <<
"' attribute must be equal to 0 or 1";
94 segmentSizes.push_back(segmentSize);
99 for (int32_t segmentSize : denseSegmentSizes.asArrayRef())
101 if (sum !=
static_cast<int32_t
>(numElements))
102 return op->
emitError() <<
"sum of elements in '" << attrName
103 <<
"' attribute must be equal to the number of "
115 StringRef attrName,
unsigned numElements,
120 int numberNonSingle = count_if(
121 variadicities, [](Variadicity v) {
return v != Variadicity::single; });
122 if (numberNonSingle > 1)
124 variadicities, segmentSizes);
127 if (numberNonSingle == 0) {
128 if (numElements != variadicities.size()) {
129 return op->
emitError() <<
"op expects exactly " << variadicities.size()
130 <<
" " << elemName <<
"s, but got " << numElements;
132 for (
size_t i = 0, e = variadicities.size(); i < e; ++i)
133 segmentSizes.push_back(1);
137 assert(numberNonSingle == 1);
141 int nonSingleSegmentSize =
static_cast<int>(numElements) -
142 static_cast<int>(variadicities.size()) + 1;
144 if (nonSingleSegmentSize < 0) {
145 return op->
emitError() <<
"op expects at least " << variadicities.size() - 1
146 <<
" " << elemName <<
"s, but got " << numElements;
150 for (Variadicity variadicity : variadicities) {
151 if (variadicity == Variadicity::single) {
152 segmentSizes.push_back(1);
158 if (nonSingleSegmentSize > 1 && variadicity == Variadicity::optional)
159 return op->
emitError() <<
"op expects at most " << variadicities.size()
160 <<
" " << elemName <<
"s, but got " << numElements;
162 segmentSizes.push_back(nonSingleSegmentSize);
217 for (
auto [name, constraint] : attributeConstrs) {
219 std::optional<NamedAttribute> actual = actualAttrs.getNamed(name);
220 if (!actual.has_value())
222 <<
"attribute " << name <<
" is expected but not provided";
225 if (failed(verifier.
verify({emitError}, actual->getValue(), constraint)))
231 for (
auto [defIndex, segmentSize] :
enumerate(operandSegmentSizes)) {
232 for (
int i = 0; i < segmentSize; i++) {
233 if (failed(verifier.
verify(
235 operandConstrs[defIndex])))
243 for (
auto [defIndex, segmentSize] :
enumerate(resultSegmentSizes)) {
244 for (
int i = 0; i < segmentSize; i++) {
245 if (failed(verifier.
verify({emitError},
247 resultConstrs[defIndex])))
258 ArrayRef<std::unique_ptr<RegionConstraint>> regionsConstraints) {
261 <<
"unexpected number of regions: expected "
262 << regionsConstraints.size() <<
" but got " << op->
getNumRegions();
265 for (
auto [constraint, region] :
266 llvm::zip(regionsConstraints, op->
getRegions()))
267 if (failed(constraint->verify(region, verifier)))
273 llvm::unique_function<LogicalResult(
Operation *)
const>
276 const DenseMap<irdl::TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types,
277 const DenseMap<irdl::AttributeOp, std::unique_ptr<DynamicAttrDefinition>>
283 if (isa<VerifyConstraintInterface>(op)) {
284 if (op.getNumResults() != 1) {
286 <<
"IRDL constraint operations must have exactly one result";
289 constrToValue.push_back(op.getResult(0));
291 if (isa<VerifyRegionInterface>(op)) {
292 if (op.getNumResults() != 1) {
294 <<
"IRDL constraint operations must have exactly one result";
297 regionToValue.push_back(op.getResult(0));
303 for (
Value v : constrToValue) {
304 VerifyConstraintInterface op =
305 cast<VerifyConstraintInterface>(v.getDefiningOp());
306 std::unique_ptr<Constraint> verifier =
307 op.getVerifier(constrToValue, types, attrs);
310 constraints.push_back(std::move(verifier));
315 for (
Value v : regionToValue) {
316 VerifyRegionInterface op = cast<VerifyRegionInterface>(v.getDefiningOp());
317 std::unique_ptr<RegionConstraint> verifier =
318 op.getVerifier(constrToValue, types, attrs);
319 regionConstraints.push_back(std::move(verifier));
326 auto operandsOp = op.getOp<OperandsOp>();
327 if (operandsOp.has_value()) {
328 operandConstraints.reserve(operandsOp->getArgs().size());
329 for (
Value operand : operandsOp->getArgs()) {
330 for (
auto [i, constr] :
enumerate(constrToValue)) {
331 if (constr == operand) {
332 operandConstraints.push_back(i);
339 for (VariadicityAttr attr : operandsOp->getVariadicity())
340 operandVariadicity.push_back(attr.getValue());
347 auto resultsOp = op.getOp<ResultsOp>();
348 if (resultsOp.has_value()) {
349 resultConstraints.reserve(resultsOp->getArgs().size());
350 for (
Value result : resultsOp->getArgs()) {
351 for (
auto [i, constr] :
enumerate(constrToValue)) {
352 if (constr == result) {
353 resultConstraints.push_back(i);
360 for (
Attribute attr : resultsOp->getVariadicity())
361 resultVariadicity.push_back(cast<VariadicityAttr>(attr).getValue());
366 auto attributesOp = op.getOp<AttributesOp>();
367 if (attributesOp.has_value()) {
369 const ArrayAttr names = attributesOp->getAttributeValueNames();
371 for (
const auto &[name, value] : llvm::zip(names, values)) {
372 for (
auto [i, constr] :
enumerate(constrToValue)) {
373 if (constr == value) {
374 attributeConstraints[cast<StringAttr>(name)] = i;
382 [constraints{std::move(constraints)},
383 regionConstraints{std::move(regionConstraints)},
384 operandConstraints{std::move(operandConstraints)},
385 operandVariadicity{std::move(operandVariadicity)},
386 resultConstraints{std::move(resultConstraints)},
387 resultVariadicity{std::move(resultVariadicity)},
388 attributeConstraints{std::move(attributeConstraints)}](
Operation *op) {
391 op, verifier, operandConstraints, operandVariadicity,
392 resultConstraints, resultVariadicity, attributeConstraints);
393 const LogicalResult opRegionVerifierResult =
395 return LogicalResult::success(opVerifierResult.succeeded() &&
396 opRegionVerifierResult.succeeded());
404 const DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types,
405 const DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>>
413 printer.printGenericOp(op);
422 auto regionVerifier = [](
Operation *op) {
return LogicalResult::success(); };
425 op.getName(), dialect, std::move(verifier), std::move(regionVerifier),
426 std::move(parser), std::move(printer));
436 DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> &types,
437 DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>> &attrs) {
438 assert((isa<AttributeOp>(attrOrTypeDef) || isa<TypeOp>(attrOrTypeDef)) &&
439 "Expected an attribute or type definition");
444 if (isa<VerifyConstraintInterface>(op)) {
445 assert(op.getNumResults() == 1 &&
446 "IRDL constraint operations must have exactly one result");
447 constrToValue.push_back(op.getResult(0));
453 for (
Value v : constrToValue) {
454 VerifyConstraintInterface op =
455 cast<VerifyConstraintInterface>(v.getDefiningOp());
456 std::unique_ptr<Constraint> verifier =
457 op.getVerifier(constrToValue, types, attrs);
460 constraints.push_back(std::move(verifier));
464 std::optional<ParametersOp> params;
465 if (
auto attr = dyn_cast<AttributeOp>(attrOrTypeDef))
466 params = attr.getOp<ParametersOp>();
467 else if (
auto type = dyn_cast<TypeOp>(attrOrTypeDef))
468 params = type.getOp<ParametersOp>();
472 if (params.has_value()) {
473 paramConstraints.reserve(params->getArgs().size());
474 for (
Value param : params->getArgs()) {
475 for (
auto [i, constr] :
enumerate(constrToValue)) {
476 if (constr == param) {
477 paramConstraints.push_back(i);
484 auto verifier = [paramConstraints{std::move(paramConstraints)},
485 constraints{std::move(constraints)}](
494 return std::move(verifier);
510 if (
auto anyOf = dyn_cast<AnyOfOp>(op)) {
512 for (
Value arg : anyOf.getArgs())
513 hasAny &=
getBases(arg.getDefiningOp(), paramIds, paramIrdlOps, isIds);
519 if (
auto allOf = dyn_cast<AllOfOp>(op))
520 return getBases(allOf.getArgs()[0].getDefiningOp(), paramIds, paramIrdlOps,
524 if (
auto params = dyn_cast<ParametricOp>(op)) {
525 SymbolRefAttr symRef = params.getBaseType();
527 assert(defOp &&
"symbol reference should refer to an existing operation");
528 paramIrdlOps.insert(defOp);
533 if (
auto is = dyn_cast<IsOp>(op)) {
541 if (
auto isA = dyn_cast<AnyOp>(op))
544 llvm_unreachable(
"unknown IRDL constraint");
563 for (
Value arg : anyOf.getArgs()) {
571 if (
getBases(argOp, argParamIds, argParamIrdlOps, argIsIds))
577 for (
TypeID id : argParamIds) {
580 bool inserted = paramIds.insert(
id).second;
588 if (paramIds.count(
id))
598 bool inserted = paramIrdlOps.insert(op).second;
611 op.walk([&](DialectOp dialectOp) {
613 StringRef dialectName = dialectOp.getName();
618 dialects.insert({dialectOp, dialect});
629 op.walk([&](TypeOp typeOp) {
632 typeOp.getName(), dialect,
636 typeDefs.try_emplace(typeOp, std::move(typeDef));
647 op.walk([&](AttributeOp attrOp) {
650 attrOp.getName(), dialect,
654 attrDefs.try_emplace(attrOp, std::move(attrDef));
665 return op.emitError(
"any_of constraints are not in the correct form");
679 typeOp, dialects[typeOp.getParentOp()], types, attrs);
682 types[typeOp]->setVerifyFn(std::move(verifier));
689 res = op.walk([&](AttributeOp attrOp) {
691 attrOp, dialects[attrOp.getParentOp()], types, attrs);
694 attrs[attrOp]->setVerifyFn(std::move(verifier));
701 res = op.walk([&](OperationOp opOp) {
702 return loadOperation(opOp, dialects[opOp.getParentOp()], types, attrs);
708 for (
auto &pair : types) {
714 for (
auto &pair : attrs) {
static bool getBases(Operation *op, SmallPtrSet< TypeID, 4 > ¶mIds, SmallPtrSet< Operation *, 4 > ¶mIrdlOps, SmallPtrSet< TypeID, 4 > &isIds)
Get the possible bases of a constraint.
static LogicalResult checkCorrectAnyOf(AnyOfOp anyOf)
Check that an any_of is in the subset IRDL can handle.
static DenseMap< AttributeOp, std::unique_ptr< DynamicAttrDefinition > > preallocateAttrDefs(ModuleOp op, DenseMap< DialectOp, ExtensibleDialect * > dialects)
Preallocate attribute definitions objects with empty verifiers.
LogicalResult getSegmentSizesFromAttr(Operation *op, StringRef elemName, StringRef attrName, unsigned numElements, ArrayRef< Variadicity > variadicities, SmallVectorImpl< int > &segmentSizes)
Get the operand segment sizes from the attribute dictionary.
static DynamicAttrDefinition::VerifierFn getAttrOrTypeVerifier(Operation *attrOrTypeDef, ExtensibleDialect *dialect, DenseMap< TypeOp, std::unique_ptr< DynamicTypeDefinition >> &types, DenseMap< AttributeOp, std::unique_ptr< DynamicAttrDefinition >> &attrs)
Get the verifier of a type or attribute definition.
static LogicalResult irdlOpVerifier(Operation *op, ConstraintVerifier &verifier, ArrayRef< size_t > operandConstrs, ArrayRef< Variadicity > operandVariadicity, ArrayRef< size_t > resultConstrs, ArrayRef< Variadicity > resultVariadicity, const DenseMap< StringAttr, size_t > &attributeConstrs)
Verify that the given operation satisfies the given constraints.
LogicalResult getOperandSegmentSizes(Operation *op, ArrayRef< Variadicity > variadicities, SmallVectorImpl< int > &segmentSizes)
Compute the segment sizes of the given operands.
static DenseMap< TypeOp, std::unique_ptr< DynamicTypeDefinition > > preallocateTypeDefs(ModuleOp op, DenseMap< DialectOp, ExtensibleDialect * > dialects)
Preallocate type definitions objects with empty verifiers.
LogicalResult getSegmentSizes(Operation *op, StringRef elemName, StringRef attrName, unsigned numElements, ArrayRef< Variadicity > variadicities, SmallVectorImpl< int > &segmentSizes)
Compute the segment sizes of the given element (operands, results).
static WalkResult loadOperation(OperationOp op, ExtensibleDialect *dialect, const DenseMap< TypeOp, std::unique_ptr< DynamicTypeDefinition >> &types, const DenseMap< AttributeOp, std::unique_ptr< DynamicAttrDefinition >> &attrs)
Define and load an operation represented by a irdl.operation operation.
static LogicalResult irdlAttrOrTypeVerifier(function_ref< InFlightDiagnostic()> emitError, ArrayRef< Attribute > params, ArrayRef< std::unique_ptr< Constraint >> constraints, ArrayRef< size_t > paramConstraints)
Verify that the given list of parameters satisfy the given constraints.
static LogicalResult irdlRegionVerifier(Operation *op, ConstraintVerifier &verifier, ArrayRef< std::unique_ptr< RegionConstraint >> regionsConstraints)
static DenseMap< DialectOp, ExtensibleDialect * > loadEmptyDialects(ModuleOp op)
Load all dialects in the given module, without loading any operation, type or attribute definitions.
LogicalResult getResultSegmentSizes(Operation *op, ArrayRef< Variadicity > variadicities, SmallVectorImpl< int > &segmentSizes)
Compute the segment sizes of the given results.
Attributes are known-constant values of operations.
TypeID getTypeID()
Return a unique identifier for the concrete attribute type.
static std::unique_ptr< DynamicAttrDefinition > get(StringRef name, ExtensibleDialect *dialect, VerifierFn &&verifier)
Create a new attribute definition at runtime.
llvm::unique_function< LogicalResult(function_ref< InFlightDiagnostic()>, ArrayRef< Attribute >) const > VerifierFn
A dialect that can be defined at runtime.
static std::unique_ptr< DynamicOpDefinition > get(StringRef name, ExtensibleDialect *dialect, OperationName::VerifyInvariantsFn &&verifyFn, OperationName::VerifyRegionInvariantsFn &&verifyRegionFn)
Create a new op at runtime.
static std::unique_ptr< DynamicTypeDefinition > get(StringRef name, ExtensibleDialect *dialect, VerifierFn &&verifier)
Create a new dynamic type definition.
A dialect that can be extended with new operations/types/attributes at runtime.
void registerDynamicOp(std::unique_ptr< DynamicOpDefinition > &&type)
Add a new operation defined at runtime to the dialect.
void registerDynamicType(std::unique_ptr< DynamicTypeDefinition > &&type)
Add a new type defined at runtime to the dialect.
void registerDynamicAttr(std::unique_ptr< DynamicAttrDefinition > &&attr)
Add a new attribute defined at runtime to the dialect.
This class represents a diagnostic that is inflight and set to be reported.
MLIRContext is the top-level object for a collection of MLIR operations.
DynamicDialect * getOrLoadDynamicDialect(StringRef dialectNamespace, function_ref< void(DynamicDialect *)> ctor)
Get (or create) a dynamic dialect for the given name.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
DictionaryAttr getAttrDictionary()
Return all of the attributes on this operation as a DictionaryAttr.
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
unsigned getNumRegions()
Returns the number of regions held by this operation.
unsigned getNumOperands()
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
operand_type_range getOperandTypes()
result_type_range getResultTypes()
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
unsigned getNumResults()
Return the number of results held by this operation.
iterator_range< OpIterator > getOps()
This class provides an efficient unique identifier for a specific C++ type.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
bool wasInterrupted() const
Returns true if the walk was interrupted.
static WalkResult interrupt()
Provides context to the verification of constraints.
LogicalResult verify(function_ref< InFlightDiagnostic()> emitError, Attribute attr, unsigned variable)
Check that a constraint is satisfied by an attribute.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
llvm::LogicalResult loadDialects(ModuleOp op)
Load all the dialects defined in the module.
Operation * lookupSymbolNearDialect(SymbolTableCollection &symbolTable, Operation *source, SymbolRefAttr symbol)
Looks up a symbol from the symbol table containing the source operation's dialect definition operatio...
llvm::unique_function< LogicalResult(Operation *) const > createVerifier(OperationOp operation, const DenseMap< irdl::TypeOp, std::unique_ptr< DynamicTypeDefinition >> &typeDefs, const DenseMap< irdl::AttributeOp, std::unique_ptr< DynamicAttrDefinition >> &attrDefs)
Generate an op verifier function from the given IRDL operation definition.
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
This represents an operation in an abstracted form, suitable for use with the builder APIs.