diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td --- a/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td +++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td @@ -161,7 +161,8 @@ def IRDL_OperationOp : IRDL_Op<"operation", [HasParent<"DialectOp">, NoTerminator, NoRegionArguments, - AtMostOneChildOf<"OperandsOp, ResultsOp, AttributesOp">, Symbol]> { + AtMostOneChildOf<"OperandsOp, ResultsOp, AttributesOp, RegionsOp">, + Symbol]> { let summary = "Define a new operation"; let description = [{ `irdl.operation` defines a new operation belonging to the `irdl.dialect` @@ -297,6 +298,37 @@ let hasVerifier = true; } +def IRDL_RegionsOp : IRDL_Op<"regions", [HasParent<"OperationOp">]> { + let summary = "Define the regions of an operation"; + let description = [{ + `irdl.regions` defines the regions of an operation by accepting + values produced by `irdl.region` operation as arguments. + + Example: + + ```mlir + irdl.dialect @example { + irdl.operation @op_with_regions { + %r1 = irdl.region with size 3 + %0 = irdl.any + %r2 = irdl.region(%0) + irdl.regions(%r1, %r2) + } + } + ``` + + In the snippet above the operation is constrained to have two regions. + The first region should contain three blocks with no agruments + in the first one. The second region should have one region + with one argument. + }]; + + let arguments = (ins Variadic:$args); + let assemblyFormat = " `(` $args `)` attr-dict "; + + let hasVerifier = true; +} + //===----------------------------------------------------------------------===// // IRDL Constraint operations //===----------------------------------------------------------------------===// @@ -306,6 +338,46 @@ DeclareOpInterfaceMethods] # traits> { } +def IRDL_RegionOp : IRDL_ConstraintOp<"region", [HasParent<"OperationOp">]> { + let summary = "Define a region of an operation"; + let description = [{ + `irdl.region` defines a set of characterstics that + a region of an operation should have. + The characteristics are the set of constraints for the entry block of + a region and the total number of blocks. The number of blocks must be + a non-zero and non-negative integer number. The number of blocks is + optional by default and equals to 1. + + + Example: + + ```mlir + irdl.dialect @example { + irdl.operation @op_with_regions { + %r1 = irdl.region with size 3 + %0 = irdl.any + %r2 = irdl.region(%0) + irdl.regions(%r1, %r2) + } + } + ``` + + In the snippet above the operation is constrained to have two regions. + The first region should contain three blocks with no agruments + in the first one. The second region should have one region + with one argument. + }]; + let arguments = (ins Variadic:$entryBlockArgs, + OptionalAttr:$numberOfBlocks); + let results = (outs IRDL_AttributeType:$output); + + let assemblyFormat = [{ + (`(` $entryBlockArgs^ `)`)? (`with` `size` $numberOfBlocks^)? attr-dict + }]; + + let hasVerifier = true; +} + def IRDL_IsOp : IRDL_ConstraintOp<"is", [ParentOneOf<["TypeOp", "AttributeOp", "OperationOp"]>, Pure]> { let summary = "Constraints an attribute/type to be a specific attribute instance"; diff --git a/mlir/include/mlir/Dialect/IRDL/IRDLVerifiers.h b/mlir/include/mlir/Dialect/IRDL/IRDLVerifiers.h --- a/mlir/include/mlir/Dialect/IRDL/IRDLVerifiers.h +++ b/mlir/include/mlir/Dialect/IRDL/IRDLVerifiers.h @@ -178,6 +178,15 @@ ConstraintVerifier &context) const override; }; +class RegionConstraint : public Constraint { +public: + virtual ~RegionConstraint() = default; + + LogicalResult verify(function_ref emitError, + Attribute attr, + ConstraintVerifier &context) const override; +}; + } // namespace irdl } // namespace mlir diff --git a/mlir/lib/Dialect/IRDL/IR/IRDL.cpp b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp --- a/mlir/lib/Dialect/IRDL/IR/IRDL.cpp +++ b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp @@ -9,10 +9,13 @@ #include "mlir/Dialect/IRDL/IR/IRDL.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/Diagnostics.h" #include "mlir/IR/DialectImplementation.h" #include "mlir/IR/ExtensibleDialect.h" #include "mlir/IR/OpDefinition.h" #include "mlir/IR/OpImplementation.h" +#include "mlir/IR/Operation.h" +#include "mlir/Support/LLVM.h" #include "mlir/Support/LogicalResult.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/TypeSwitch.h" @@ -114,6 +117,31 @@ p << '}'; } +LogicalResult RegionOp::verify() { + if (IntegerAttr numberOfBlocks = getNumberOfBlocksAttr()) { + if (int64_t number = numberOfBlocks.getInt(); number <= 0) { + return emitOpError("the number of blocks is expected to be >= 1 but got ") + << number; + } + } else { + setNumberOfBlocks(1); + } + return success(); +} + +LogicalResult RegionsOp::verify() { + for (auto [i, arg] : llvm::enumerate(getArgs())) { + mlir::Operation *op = arg.getDefiningOp(); + if (llvm::isa(op)) { + continue; + } + return emitOpError( + "expected only `irdl.region` results as operands but got ") + << op->getName() << " in the operand number " << i; + } + return success(); +} + #include "mlir/Dialect/IRDL/IR/IRDLInterfaces.cpp.inc" #define GET_TYPEDEF_CLASSES diff --git a/mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp b/mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp --- a/mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp +++ b/mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp @@ -98,3 +98,11 @@ &attrs) { return std::make_unique(); } + +std::unique_ptr RegionOp::getVerifier( + ArrayRef valueToConstr, + DenseMap> const &types, + DenseMap> const + &attrs) { + return std::make_unique(); +} diff --git a/mlir/lib/Dialect/IRDL/IRDLVerifiers.cpp b/mlir/lib/Dialect/IRDL/IRDLVerifiers.cpp --- a/mlir/lib/Dialect/IRDL/IRDLVerifiers.cpp +++ b/mlir/lib/Dialect/IRDL/IRDLVerifiers.cpp @@ -175,3 +175,9 @@ ConstraintVerifier &context) const { return success(); } + +LogicalResult +RegionConstraint::verify(function_ref emitError, + Attribute attr, ConstraintVerifier &context) const { + return success(); +} diff --git a/mlir/test/Dialect/IRDL/regions.irdl.mlir b/mlir/test/Dialect/IRDL/regions.irdl.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/IRDL/regions.irdl.mlir @@ -0,0 +1,19 @@ +// RUN: mlir-opt %s -verify-diagnostics -split-input-file + +irdl.dialect @testRegionOpNegativeNumber { + irdl.operation @op { + // expected-error @below {{'irdl.region' op the number of blocks is expected to be >= 1 but got -42}} + %r1 = irdl.region with size -42 + } +} + +// ----- + +irdl.dialect @testRegionsOpWrongOperation { + irdl.operation @op { + %r1 = irdl.any + + // expected-error @below {{'irdl.regions' op expected only `irdl.region` results as operands but got irdl.any in the operand number 0}} + irdl.regions(%r1) + } +} diff --git a/mlir/test/Dialect/IRDL/testd.irdl.mlir b/mlir/test/Dialect/IRDL/testd.irdl.mlir --- a/mlir/test/Dialect/IRDL/testd.irdl.mlir +++ b/mlir/test/Dialect/IRDL/testd.irdl.mlir @@ -119,4 +119,19 @@ "attr2" = %1 } } + // CHECK: irdl.operation @regions { + // CHECK: %[[r0:[^ ]*]] = irdl.region with size 1 + // CHECK: %[[v0:[^ ]*]] = irdl.any + // CHECK: %[[r1:[^ ]*]] = irdl.region(%[[v0]]) + // CHECK: %[[r2:[^ ]*]] = irdl.region with size 3 + // CHECK: irdl.regions(%[[r0]], %[[r1]], %[[r2]]) + // CHECK: } + irdl.operation @regions { + %r0 = irdl.region with size 1 + %v0 = irdl.any + %r1 = irdl.region(%v0) + %r2 = irdl.region with size 3 + + irdl.regions(%r0, %r1, %r2) + } } diff --git a/mlir/test/Dialect/IRDL/testd.mlir b/mlir/test/Dialect/IRDL/testd.mlir --- a/mlir/test/Dialect/IRDL/testd.mlir +++ b/mlir/test/Dialect/IRDL/testd.mlir @@ -234,3 +234,30 @@ "testd.attrs"() {attr1 = i32, attr2 = i32} : () -> () return } + +// ----- + +//===----------------------------------------------------------------------===// +// Regions +//===----------------------------------------------------------------------===// + +func.func @succeededRegions() { + "testd.regions"() ({ + ^bb1: + llvm.unreachable + }, + { + ^bb1(%arg0: i32): + llvm.unreachable + }, + { + ^bb1: + cf.br ^bb3 + ^bb2: + cf.br ^bb3 + ^bb3: + llvm.unreachable + }) : () -> () + + return +} \ No newline at end of file