diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h --- a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h @@ -16,6 +16,7 @@ #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/Dialect.h" #include "mlir/IR/OpDefinition.h" +#include "mlir/IR/SymbolTable.h" #include "mlir/Dialect/OpenACC/OpenACCOpsDialect.h.inc" #include "mlir/Dialect/OpenACC/OpenACCOpsEnums.h.inc" diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td --- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td @@ -18,6 +18,7 @@ include "mlir/IR/BuiltinTypes.td" include "mlir/IR/EnumAttr.td" include "mlir/IR/OpBase.td" +include "mlir/IR/SymbolInterfaces.td" include "mlir/Dialect/OpenACC/OpenACCBase.td" include "mlir/Dialect/OpenACC/OpenACCOpsTypes.td" include "mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td" @@ -399,6 +400,63 @@ }]; } +//===----------------------------------------------------------------------===// +// 2.5.13 private clause +//===----------------------------------------------------------------------===// + +def OpenACC_PrivateRecipeOp : OpenACC_Op<"private.recipe", + [IsolatedFromAbove, Symbol]> { + let summary = "privatization recipe"; + + let description = [{ + Declares an OpenACC privatization recipe. The operation requires one + mandatory and one optional region. + + 1. The initializer region specifies how to allocate and initialize a new + private value. For example in Fortran, a derived-type might have a + default initialization. The region has an argument that contains the + value that need to be privatized. This is useful if the type is not + known at compile time and the private value is needed to create its + copy. + 2. The destroy region specifies how to destruct the value when it reaches + its end of life. It takes the privatized value as argument. + + A single privatization recipe can be used for multiple operand if they have + the same type and do not require a specific default initialization. + + Example: + + ```mlir + acc.private.recipe @privatization_f32 : f32 init { + ^bb0(%0: f32): + // init region contains a sequence of operations to create and + // initialize the copy if needed. It yields the create copy. + } destroy { + ^bb0(%0: f32) + // destroy region contains a sequences of operations to destruct the + // created copy. + } + + // The privatization symbol is then used in the corresponding operation. + acc.parallel private(@privatization_f32 -> %a : f32) { + } + ``` + }]; + + let arguments = (ins SymbolNameAttr:$sym_name, + TypeAttr:$type); + + let regions = (region AnyRegion:$initRegion, + AnyRegion:$destroyRegion); + + let assemblyFormat = [{ + $sym_name `:` $type attr-dict-with-keyword `init` $initRegion + (`destroy` $destroyRegion^)? + }]; + + let hasRegionVerifier = 1; +} + //===----------------------------------------------------------------------===// // 2.5.1 parallel Construct //===----------------------------------------------------------------------===// @@ -863,7 +921,7 @@ // Yield operation for the acc.loop and acc.parallel operations. def OpenACC_YieldOp : OpenACC_Op<"yield", [ReturnLike, Terminator, - ParentOneOf<["ParallelOp, LoopOp, SerialOp"]>]> { + ParentOneOf<["ParallelOp, LoopOp, SerialOp, PrivateRecipeOp"]>]> { let summary = "Acc yield and termination operation"; let description = [{ diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp --- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp +++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp @@ -334,6 +334,38 @@ } // namespace +//===----------------------------------------------------------------------===// +// PrivateRecipeOp +//===----------------------------------------------------------------------===// + +LogicalResult acc::PrivateRecipeOp::verifyRegions() { + if (getInitRegion().empty()) + return emitOpError() << "expects non-empty init region"; + Block &initBlock = getInitRegion().front(); + if (initBlock.getNumArguments() != 1 || + initBlock.getArgument(0).getType() != getType()) + return emitOpError() << "expects init region with one argument of the " + << "privatization type"; + + for (YieldOp yieldOp : getInitRegion().getOps()) { + if (yieldOp.getOperands().size() != 1 || + yieldOp.getOperands().getTypes()[0] != getType()) + return emitOpError() << "expects init region to yield a value " + "of the privatization type"; + } + + // Destroy region is optional. + if (getDestroyRegion().empty()) + return success(); + + Block &destroyBlock = getDestroyRegion().front(); + if (destroyBlock.getNumArguments() != 1 || + destroyBlock.getArgument(0).getType() != getType()) + return emitOpError() << "expects destroy region with one argument of the " + << "privatization type"; + return success(); +} + //===----------------------------------------------------------------------===// // ParallelOp //===----------------------------------------------------------------------===// diff --git a/mlir/test/Dialect/OpenACC/invalid.mlir b/mlir/test/Dialect/OpenACC/invalid.mlir --- a/mlir/test/Dialect/OpenACC/invalid.mlir +++ b/mlir/test/Dialect/OpenACC/invalid.mlir @@ -258,3 +258,48 @@ acc.kernels dataOperands(%value : memref<10xf32>) { acc.yield } + +// ----- + +// expected-error@+1 {{expects non-empty init region}} +acc.private.recipe @privatization_i32 : !llvm.ptr init { +} + +// ----- + +// expected-error@+1 {{expects init region with one argument of the privatization type}} +acc.private.recipe @privatization_i32 : !llvm.ptr init { +^bb0(%arg0 : !llvm.ptr): + %c1 = arith.constant 1 : i32 + %c0 = arith.constant 0 : i32 + %0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr + llvm.store %c0, %0 : !llvm.ptr + acc.yield %0 : !llvm.ptr +} + +// ----- + +// expected-error@+1 {{expects init region to yield a value of the privatization type}} +acc.private.recipe @privatization_i32 : !llvm.ptr init { +^bb0(%arg0 : !llvm.ptr): + %c1 = arith.constant 1 : i32 + %c0 = arith.constant 0 : i32 + %0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr + llvm.store %c0, %0 : !llvm.ptr + acc.yield %0 : !llvm.ptr +} + +// ----- + +// expected-error@+1 {{expects destroy region with one argument of the privatization type}} +acc.private.recipe @privatization_i32 : !llvm.ptr init { +^bb0(%arg0 : !llvm.ptr): + %c1 = arith.constant 1 : i32 + %c0 = arith.constant 0 : i32 + %0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr + llvm.store %c0, %0 : !llvm.ptr + acc.yield %0 : !llvm.ptr +} destroy { +^bb0(%arg0 : f32): + "test.openacc_dummy_op"(%arg0) : (f32) -> () +} diff --git a/mlir/test/Dialect/OpenACC/ops.mlir b/mlir/test/Dialect/OpenACC/ops.mlir --- a/mlir/test/Dialect/OpenACC/ops.mlir +++ b/mlir/test/Dialect/OpenACC/ops.mlir @@ -1287,3 +1287,51 @@ // CHECK: acc.host_data dataOperands(%[[PTR]] : !llvm.ptr) { // CHECK: } attributes {if_present} // CHECK: acc.host_data if(%[[IFCOND]]) dataOperands(%[[PTR]] : !llvm.ptr) + +// ----- + +acc.private.recipe @privatization_i32 : !llvm.ptr init { +^bb0(%arg0: !llvm.ptr): + %c1 = arith.constant 1 : i32 + %c0 = arith.constant 0 : i32 + %0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr + llvm.store %c0, %0 : !llvm.ptr + acc.yield %0 : !llvm.ptr +} + +// CHECK: acc.private.recipe @privatization_i32 : !llvm.ptr init { +// CHECK: %[[C1:.*]] = arith.constant 1 : i32 +// CHECK: %[[C0:.*]] = arith.constant 0 : i32 +// CHECK: %[[ALLOCA:.*]] = llvm.alloca %[[C1]] x i32 : (i32) -> !llvm.ptr +// CHECK: llvm.store %[[C0]], %[[ALLOCA]] : !llvm.ptr +// CHECK: acc.yield %[[ALLOCA]] : !llvm.ptr + +// ----- + +func.func private @destroy_struct(!llvm.struct<(i32, i32)>) -> () + +acc.private.recipe @privatization_struct_i32_i64 : !llvm.struct<(i32, i32)> init { +^bb0(%arg0 : !llvm.struct<(i32, i32)>): + %c1 = arith.constant 1 : i32 + %0 = llvm.mlir.undef : !llvm.struct<(i32, i32)> + %1 = llvm.insertvalue %c1, %0[0] : !llvm.struct<(i32, i32)> + %2 = llvm.insertvalue %c1, %1[1] : !llvm.struct<(i32, i32)> + acc.yield %2 : !llvm.struct<(i32, i32)> +} destroy { +^bb0(%arg0: !llvm.struct<(i32, i32)>): + func.call @destroy_struct(%arg0) : (!llvm.struct<(i32, i32)>) -> () + acc.terminator +} + +// CHECK: func.func private @destroy_struct(!llvm.struct<(i32, i32)>) + +// CHECK: acc.private.recipe @privatization_struct_i32_i64 : !llvm.struct<(i32, i32)> init { +// CHECK: %[[C1:.*]] = arith.constant 1 : i32 +// CHECK: %[[UNDEF:.*]] = llvm.mlir.undef : !llvm.struct<(i32, i32)> +// CHECK: %[[UNDEF1:.*]] = llvm.insertvalue %[[C1]], %[[UNDEF]][0] : !llvm.struct<(i32, i32)> +// CHECK: %[[UNDEF2:.*]] = llvm.insertvalue %[[C1]], %[[UNDEF1]][1] : !llvm.struct<(i32, i32)> +// CHECK: acc.yield %[[UNDEF2]] : !llvm.struct<(i32, i32)> +// CHECK: } destroy { +// CHECK: ^bb0(%[[ARG0:.*]]: !llvm.struct<(i32, i32)>): +// CHECK: func.call @destroy_struct(%[[ARG0]]) : (!llvm.struct<(i32, i32)>) -> () +// CHECK: acc.terminator