diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td index 5da05476a6837..8fbc680f26bab 100644 --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td @@ -619,7 +619,7 @@ def SimdLoopOp : OpenMP_Op<"simdloop", [AttrSizedOperandSegments, def YieldOp : OpenMP_Op<"yield", [Pure, ReturnLike, Terminator, ParentOneOf<["WsLoopOp", "ReductionDeclareOp", - "AtomicUpdateOp", "SimdLoopOp"]>]> { + "AtomicUpdateOp", "SimdLoopOp", "StructuredRegionOp"]>]> { let summary = "loop yield and termination operation"; let description = [{ "omp.yield" yields SSA values from the OpenMP dialect op region and @@ -1987,4 +1987,40 @@ def ClauseRequiresAttr : EnumAttr { } + +def StructuredRegionOp : OpenMP_Op<"structured_region"> { + let summary = "Encapsulates a region in an OpenMP construct."; + let description = [{ + Encapsulates a region surrounded by an OpenMP Construct. The intended use + of this operation is that within an OpenMP operation's region, there would + be a single `omp.structured_region` operation and a terminator operation as + shown below. + + ``` + // Example with `omp.sections` + omp.sections { + %x = omp.structured_region { + %t = call @foo : () -> (i32) + omp.yield(%t : i32) + } + omp.terminator + } + ``` + + This operation is especially more useful in operations that use `omp.yield` + as a terminator. For example, in the proposed canonical loop operation, + this operation would help fix the arguments of the yield operation and it + is not affected by branches in the region assosciated with the canonical + loop. In cases where the yielded value has to be a compile time constant, + this provides a mechanism to avoid complex static analysis for the constant + value. + }]; + let regions = (region AnyRegion:$region); + let results = (outs Variadic:$result); + let assemblyFormat = [{ + ( `(` type($result)^ `)` )? $region attr-dict + }]; + let hasVerifier = 1; +} + #endif // OPENMP_OPS diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp index 2bf9355ed6267..e00c4c44c60dc 100644 --- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp +++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp @@ -1468,6 +1468,18 @@ LogicalResult CancellationPointOp::verify() { return success(); } +//===----------------------------------------------------------------------===// +// RegionOp +//===----------------------------------------------------------------------===// + +LogicalResult StructuredRegionOp::verify() { + Operation *parentOp = (*this)->getParentOp(); + assert(parentOp && "'omp.region' op must have a parent"); + if (!isa(parentOp->getDialect())) + return emitOpError() << "must be used under an OpenMP Dialect operation"; + return success(); +} + //===----------------------------------------------------------------------===// // DataBoundsOp //===----------------------------------------------------------------------===// diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir index c8025249e2700..e4c99304010b6 100644 --- a/mlir/test/Dialect/OpenMP/invalid.mlir +++ b/mlir/test/Dialect/OpenMP/invalid.mlir @@ -1657,3 +1657,25 @@ func.func @omp_target_exit_data(%map1: memref) { } llvm.mlir.global internal @_QFsubEx() : i32 + +// ----- + +func.func @omp_region_invalid() { + // expected-error @below {{'omp.structured_region' op must be used under an OpenMP Dialect operation}} + omp.structured_region { + omp.yield + } + return +} + +// ----- + +func.func @omp_region_invalid(%c: i1) { + scf.if %c { + // expected-error @below {{'omp.structured_region' op must be used under an OpenMP Dialect operation}} + omp.structured_region { + omp.yield + } + } + return +} diff --git a/mlir/test/Dialect/OpenMP/structured_region.mlir b/mlir/test/Dialect/OpenMP/structured_region.mlir new file mode 100644 index 0000000000000..13fc4a0d7284a --- /dev/null +++ b/mlir/test/Dialect/OpenMP/structured_region.mlir @@ -0,0 +1,53 @@ +// RUN: mlir-opt %s | mlir-opt | FileCheck %s + +// CHECK-LABEL: @basic_omp_region +// CHECK-NEXT: omp.parallel { +// CHECK-NEXT: {{.+}} = omp.structured_region(i32) { +// CHECK-NEXT: {{.+}} = "test.foo"() : () -> i32 +// CHECK-NEXT: omp.yield({{.+}}) +// CHECK-NEXT: } +// CHECK-NEXT: omp.terminator +// CHECK-NEXT: } +// CHECK-NEXT: return +func.func @basic_omp_region() { + omp.parallel { + %x = omp.structured_region (i32) { + %y = "test.foo"() : () -> i32 + omp.yield(%y : i32) + } + omp.terminator + } + return +} + +// CHECK-LABEL: @omp_region_with_branch +// CHECK-NEXT: omp.task { +// CHECK-NEXT: {{.+}} = omp.structured_region(i32) { +// CHECK-NEXT: %[[c:.*]] = "test.foo"() : () -> i1 +// CHECK-NEXT: cf.cond_br %[[c]], ^[[bb1:.*]](%[[c]] : i1), ^[[bb2:.*]](%[[c]] : i1) +// CHECK-NEXT: ^[[bb1]](%[[arg:.*]]: i1): +// CHECK-NEXT: {{.+}} = "test.bar"() : () -> i32 +// CHECK-NEXT: omp.yield({{.+}}) +// CHECK-NEXT: ^[[bb2]](%[[arg2:.*]]: i1): +// CHECK-NEXT: {{.+}} = "test.baz"() : () -> i32 +// CHECK-NEXT: omp.yield({{.+}}) +// CHECK-NEXT: } +// CHECK-NEXT: omp.terminator +// CHECK-NEXT: } +// CHECK-NEXT: return +func.func @omp_region_with_branch(%a: i32) { + omp.task { + %t = omp.structured_region (i32) { + %c = "test.foo"() : () -> i1 + cf.cond_br %c, ^bb1(%c: i1), ^bb2(%c: i1) + ^bb1(%arg: i1): + %x = "test.bar"() : () -> i32 + omp.yield(%x : i32) + ^bb2(%arg2: i1): + %y = "test.baz"() : () -> i32 + omp.yield(%y: i32) + } + omp.terminator + } + return +}