[mlir][openacc] Add separate acc data operations for OpenACC data clauses
authorRazvan Lupusoru <razvan.lupusoru@gmail.com>
Tue, 18 Apr 2023 00:01:51 +0000 (17:01 -0700)
committerRazvan Lupusoru <rlupusoru@nvidia.com>
Tue, 18 Apr 2023 22:36:38 +0000 (15:36 -0700)
As outlined in [1], data clauses are now implemented as separate operations
from the constructs that they belong to. Some of the highlighted benefits:
- Correctly represent dataflow of data operations
- Easier to track debugging information
- Friendlier to add attributes and to optimize operations

For now, all of the other operand lists are being kept until all references
to them in LLVM can be removed (such as those in flang lowering)

[1] https://discourse.llvm.org/t/rfc-openacc-dialect-data-operation-improvements/69825

Reviewed By: clementval, vzakhari

Differential Revision: https://reviews.llvm.org/D148389

flang/lib/Lower/OpenACC.cpp
mlir/include/mlir/Dialect/OpenACC/CMakeLists.txt
mlir/include/mlir/Dialect/OpenACC/OpenACC.h
mlir/include/mlir/Dialect/OpenACC/OpenACCBase.td [new file with mode: 0644]
mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
mlir/include/mlir/Dialect/OpenACC/OpenACCOpsTypes.td [new file with mode: 0644]
mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td [new file with mode: 0644]
mlir/lib/Dialect/OpenACC/CMakeLists.txt
mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
mlir/test/Dialect/OpenACC/ops.mlir

index 3a48177..d17dab6 100644 (file)
@@ -422,7 +422,7 @@ createComputeOp(Fortran::lower::AbstractConverter &converter,
       copyOperands, copyinOperands, copyinReadonlyOperands, copyoutOperands,
       copyoutZeroOperands, createOperands, createZeroOperands, noCreateOperands,
       presentOperands, devicePtrOperands, attachOperands, firstprivateOperands,
-      privateOperands;
+      privateOperands, dataClauseOperands;
 
   // Async, wait and self clause have optional values but can be present with
   // no value as well. When there is no value, the op has an attribute to
@@ -580,6 +580,7 @@ createComputeOp(Fortran::lower::AbstractConverter &converter,
     addOperands(operands, operandSegments, privateOperands);
     addOperands(operands, operandSegments, firstprivateOperands);
   }
+  addOperands(operands, operandSegments, dataClauseOperands);
 
   Op computeOp;
   if constexpr (std::is_same_v<Op, mlir::acc::KernelsOp>)
@@ -608,7 +609,7 @@ static void genACCDataOp(Fortran::lower::AbstractConverter &converter,
   llvm::SmallVector<mlir::Value> copyOperands, copyinOperands,
       copyinReadonlyOperands, copyoutOperands, copyoutZeroOperands,
       createOperands, createZeroOperands, noCreateOperands, presentOperands,
-      deviceptrOperands, attachOperands;
+      deviceptrOperands, attachOperands, dataClauseOperands;
 
   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
 
@@ -680,6 +681,7 @@ static void genACCDataOp(Fortran::lower::AbstractConverter &converter,
   addOperands(operands, operandSegments, presentOperands);
   addOperands(operands, operandSegments, deviceptrOperands);
   addOperands(operands, operandSegments, attachOperands);
+  addOperands(operands, operandSegments, dataClauseOperands);
 
   createRegionOp<mlir::acc::DataOp, mlir::acc::TerminatorOp>(
       firOpBuilder, currentLocation, operands, operandSegments);
@@ -761,7 +763,7 @@ genACCEnterDataOp(Fortran::lower::AbstractConverter &converter,
                   const Fortran::parser::AccClauseList &accClauseList) {
   mlir::Value ifCond, async, waitDevnum;
   llvm::SmallVector<mlir::Value> copyinOperands, createOperands,
-      createZeroOperands, attachOperands, waitOperands;
+      createZeroOperands, attachOperands, waitOperands, dataClauseOperands;
 
   // Async, wait and self clause have optional values but can be present with
   // no value as well. When there is no value, the op has an attribute to
@@ -821,6 +823,7 @@ genACCEnterDataOp(Fortran::lower::AbstractConverter &converter,
   addOperands(operands, operandSegments, createOperands);
   addOperands(operands, operandSegments, createZeroOperands);
   addOperands(operands, operandSegments, attachOperands);
+  addOperands(operands, operandSegments, dataClauseOperands);
 
   mlir::acc::EnterDataOp enterDataOp = createSimpleOp<mlir::acc::EnterDataOp>(
       firOpBuilder, currentLocation, operands, operandSegments);
@@ -839,7 +842,7 @@ genACCExitDataOp(Fortran::lower::AbstractConverter &converter,
                  const Fortran::parser::AccClauseList &accClauseList) {
   mlir::Value ifCond, async, waitDevnum;
   llvm::SmallVector<mlir::Value> copyoutOperands, deleteOperands,
-      detachOperands, waitOperands;
+      detachOperands, waitOperands, dataClauseOperands;
 
   // Async and wait clause have optional values but can be present with
   // no value as well. When there is no value, the op has an attribute to
@@ -897,6 +900,7 @@ genACCExitDataOp(Fortran::lower::AbstractConverter &converter,
   addOperands(operands, operandSegments, copyoutOperands);
   addOperands(operands, operandSegments, deleteOperands);
   addOperands(operands, operandSegments, detachOperands);
+  addOperands(operands, operandSegments, dataClauseOperands);
 
   mlir::acc::ExitDataOp exitDataOp = createSimpleOp<mlir::acc::ExitDataOp>(
       firOpBuilder, currentLocation, operands, operandSegments);
index 7170c2c..119885e 100644 (file)
@@ -2,15 +2,25 @@ set(LLVM_TARGET_DEFINITIONS ${LLVM_MAIN_INCLUDE_DIR}/llvm/Frontend/OpenACC/ACC.t
 mlir_tablegen(AccCommon.td --gen-directive-decl --directives-dialect=OpenACC)
 add_public_tablegen_target(acc_common_td)
 
+add_mlir_dialect(OpenACCOps acc)
+
+add_mlir_doc(OpenACCOps OpenACCDialect Dialects/ -gen-dialect-doc)
+add_dependencies(OpenACCDialectDocGen acc_common_td)
+
 set(LLVM_TARGET_DEFINITIONS OpenACCOps.td)
-mlir_tablegen(OpenACCOpsDialect.h.inc -gen-dialect-decls -dialect=acc)
-mlir_tablegen(OpenACCOpsDialect.cpp.inc -gen-dialect-defs -dialect=acc)
-mlir_tablegen(OpenACCOps.h.inc -gen-op-decls)
-mlir_tablegen(OpenACCOps.cpp.inc -gen-op-defs)
 mlir_tablegen(OpenACCOpsEnums.h.inc -gen-enum-decls)
 mlir_tablegen(OpenACCOpsEnums.cpp.inc -gen-enum-defs)
+add_public_tablegen_target(MLIROpenACCEnumsIncGen)
+add_dependencies(mlir-headers MLIROpenACCEnumsIncGen)
+
+set(LLVM_TARGET_DEFINITIONS OpenACCOps.td)
 mlir_tablegen(OpenACCOpsAttributes.h.inc -gen-attrdef-decls -attrdefs-dialect=acc)
 mlir_tablegen(OpenACCOpsAttributes.cpp.inc -gen-attrdef-defs -attrdefs-dialect=acc)
-add_mlir_doc(OpenACCOps OpenACCDialect Dialects/ -gen-dialect-doc)
-add_public_tablegen_target(MLIROpenACCOpsIncGen)
-add_dependencies(OpenACCDialectDocGen acc_common_td)
+add_public_tablegen_target(MLIROpenACCAttributesIncGen)
+add_dependencies(mlir-headers MLIROpenACCAttributesIncGen)
+
+set(LLVM_TARGET_DEFINITIONS OpenACCTypeInterfaces.td)
+mlir_tablegen(OpenACCTypeInterfaces.h.inc -gen-type-interface-decls)
+mlir_tablegen(OpenACCTypeInterfaces.cpp.inc -gen-type-interface-defs)
+add_public_tablegen_target(MLIROpenACCTypeInterfacesIncGen)
+add_dependencies(mlir-headers MLIROpenACCTypeInterfacesIncGen)
index 3e0ed12..4e7db76 100644 (file)
 #ifndef MLIR_DIALECT_OPENACC_OPENACC_H_
 #define MLIR_DIALECT_OPENACC_OPENACC_H_
 
+#include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/Dialect.h"
 #include "mlir/IR/OpDefinition.h"
 
 #include "mlir/Dialect/OpenACC/OpenACCOpsDialect.h.inc"
 #include "mlir/Dialect/OpenACC/OpenACCOpsEnums.h.inc"
+#include "mlir/Dialect/OpenACC/OpenACCTypeInterfaces.h.inc"
+#include "mlir/Interfaces/SideEffectInterfaces.h"
+
+#define GET_TYPEDEF_CLASSES
+#include "mlir/Dialect/OpenACC/OpenACCOpsTypes.h.inc"
 
 #define GET_ATTRDEF_CLASSES
 #include "mlir/Dialect/OpenACC/OpenACCOpsAttributes.h.inc"
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCBase.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCBase.td
new file mode 100644 (file)
index 0000000..7bbe2cc
--- /dev/null
@@ -0,0 +1,33 @@
+//===- OpenACCBase.td - OpenACC dialect definition ---------*- tablegen -*-===//
+//
+// Part of the MLIR Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// =============================================================================
+//
+// Defines MLIR OpenACC dialect.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef OPENACC_BASE
+#define OPENACC_BASE
+
+include "mlir/IR/AttrTypeBase.td"
+
+def OpenACC_Dialect : Dialect {
+  let name = "acc";
+
+  let summary = "An OpenACC dialect for MLIR.";
+
+  let description = [{
+    This dialect models the construct from the OpenACC 3.1 directive language.
+  }];
+
+  let useDefaultAttributePrinterParser = 1;
+  let useDefaultTypePrinterParser = 1;
+  let cppNamespace = "::mlir::acc";
+  let dependentDialects = ["::mlir::memref::MemRefDialect","::mlir::LLVM::LLVMDialect"];
+}
+
+#endif // OPENACC_BASE
index 1e8e1de..405ff78 100644 (file)
@@ -1,4 +1,4 @@
-//===- OpenACC.td - OpenACC operation definitions ----------*- tablegen -*-===//
+//===- OpenACCOps.td - OpenACC operation definitions -------*- tablegen -*-===//
 //
 // Part of the MLIR Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 #ifndef OPENACC_OPS
 #define OPENACC_OPS
 
+include "mlir/Interfaces/SideEffectInterfaces.td"
+include "mlir/IR/BuiltinTypes.td"
 include "mlir/IR/EnumAttr.td"
 include "mlir/IR/OpBase.td"
-
-def OpenACC_Dialect : Dialect {
-  let name = "acc";
-
-  let summary = "An OpenACC dialect for MLIR.";
-
-  let description = [{
-    This dialect models the construct from the OpenACC 3.1 directive language.
-  }];
-
-  let useDefaultAttributePrinterParser = 1;
-  let cppNamespace = "::mlir::acc";
-}
+include "mlir/Dialect/OpenACC/OpenACCBase.td"
+include "mlir/Dialect/OpenACC/OpenACCOpsTypes.td"
+include "mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td"
 
 // AccCommon requires definition of OpenACC_Dialect.
 include "mlir/Dialect/OpenACC/AccCommon.td"
@@ -66,12 +58,241 @@ def OpenACC_ReductionOpAttr : EnumAttr<OpenACC_Dialect, OpenACC_ReductionOp,
 // Type used in operation below.
 def IntOrIndex : AnyTypeOf<[AnyInteger, Index]>;
 
+// Simple alias to pointer-like interface to reduce verbosity.
+def OpenACC_PointerLikeType : TypeAlias<OpenACC_PointerLikeTypeInterface,
+       "pointer-like type">;
+
+// Define the OpenACC data clauses. There are a few cases where a modifier
+// is used, like create(zero), copyin(readonly), and copyout(zero). Since in
+// some cases we decompose the original acc data clauses into multiple acc
+// dialect operations, we need to keep track of original clause. Thus even
+// for the clause with modifier, we create separate operation to make this
+// possible.
+def OpenACC_CopyinClause          : I64EnumAttrCase<"acc_copyin", 1>;
+def OpenACC_CopyinReadonlyClause  : I64EnumAttrCase<"acc_copyin_readonly", 2>;
+def OpenACC_CopyClause            : I64EnumAttrCase<"acc_copy", 3>;
+def OpenACC_CopyoutClause         : I64EnumAttrCase<"acc_copyout", 4>;
+def OpenACC_CopyoutZeroClause     : I64EnumAttrCase<"acc_copyout_zero", 5>;
+def OpenACC_PresentClause         : I64EnumAttrCase<"acc_present", 6>;
+def OpenACC_CreateClause          : I64EnumAttrCase<"acc_create", 7>;
+def OpenACC_CreateZeroClause      : I64EnumAttrCase<"acc_create_zero", 8>;
+def OpenACC_DeleteClause          : I64EnumAttrCase<"acc_delete", 9>;
+def OpenACC_AttachClause          : I64EnumAttrCase<"acc_attach", 10>;
+def OpenACC_DetachClause          : I64EnumAttrCase<"acc_detach", 11>;
+def OpenACC_NoCreateClause        : I64EnumAttrCase<"acc_no_create", 12>;
+def OpenACC_PrivateClause         : I64EnumAttrCase<"acc_private", 13>;
+def OpenACC_FirstPrivateClause    : I64EnumAttrCase<"acc_firstprivate", 14>;
+def OpenACC_IsDevicePtrClause     : I64EnumAttrCase<"acc_deviceptr", 15>;
+def OpenACC_GetDevicePtrClause    : I64EnumAttrCase<"acc_getdeviceptr", 16>;
+
+def OpenACC_DataClauseEnum : I64EnumAttr<"DataClause",
+    "data clauses supported by OpenACC",
+    [OpenACC_CopyinClause, OpenACC_CopyinReadonlyClause, OpenACC_CopyClause,
+     OpenACC_CopyoutClause, OpenACC_CopyoutZeroClause, OpenACC_PresentClause,
+     OpenACC_CreateClause, OpenACC_CreateZeroClause, OpenACC_DeleteClause,
+     OpenACC_AttachClause, OpenACC_DetachClause, OpenACC_NoCreateClause,
+     OpenACC_PrivateClause, OpenACC_FirstPrivateClause,
+     OpenACC_IsDevicePtrClause, OpenACC_GetDevicePtrClause
+    ]> {
+  let cppNamespace = "::mlir::acc";
+}
+
+// Used for data specification in data clauses (2.7.1).
+// Either (or both) extent and upperbound must be specified.
+def OpenACC_DataBoundsOp : OpenACC_Op<"bounds",
+    [AttrSizedOperandSegments, NoMemoryEffect]> {
+  let summary = "Represents bounds information for acc data clause.";
+
+  let arguments = (ins Optional<AnyType>:$lowerbound,
+                       Optional<AnyType>:$upperbound,
+                       Optional<AnyType>:$extent,
+                       Optional<AnyType>:$stride,
+                       DefaultValuedAttr<BoolAttr, "false">:$strideInBytes,
+                       Optional<AnyType>:$startIdx);
+  let results = (outs OpenACC_DataBoundsType:$result);
+
+  let assemblyFormat = [{
+    oilist(
+        `lowerbound` `(` $lowerbound `:` type($lowerbound) `)`
+      | `upperbound` `(` $upperbound `:` type($upperbound) `)`
+      | `extent` `(` $extent `:` type($extent) `)`
+      | `stride` `(` $stride `:` type($stride) `)`
+      | `startIdx` `(` $startIdx `:` type($startIdx) `)`
+    ) attr-dict
+  }];
+
+  let hasVerifier = 1;
+}
+
+// Data entry operation does not refer to OpenACC spec terminology, but to
+// terminology used in this dialect. It refers to data operations that will
+// appear before data or compute region. It will be used as the base of acc
+// dialect operations for the following OpenACC data clauses: copyin, create,
+// present, attach, deviceptr.
+//
+// The bounds are represented in rank order. Rank 0 (inner-most dimension) is
+// the first.
+class OpenACC_DataEntryOp<string mnemonic, string clause, list<Trait> traits = []> :
+    OpenACC_Op<mnemonic, !listconcat(traits,
+        [AttrSizedOperandSegments])> {
+  let arguments = (ins OpenACC_PointerLikeTypeInterface:$varPtr,
+                       Optional<OpenACC_PointerLikeTypeInterface>:$varPtrPtr,
+                       Variadic<OpenACC_DataBoundsType>:$bounds, /* rank-0 to rank-{n-1} */
+                       DefaultValuedAttr<OpenACC_DataClauseEnum,clause>:$dataClause,
+                       OptionalAttr<OpenACC_DataClauseEnum>:$decomposedFrom,
+                       DefaultValuedAttr<BoolAttr, "true">:$structured,
+                       DefaultValuedAttr<BoolAttr, "false">:$implicit,
+                       OptionalAttr<StrAttr>:$name);
+  let results = (outs OpenACC_PointerLikeTypeInterface:$accPtr);
+
+  let assemblyFormat = [{
+    `varPtr` `(` $varPtr `:` type($varPtr) `)`
+    oilist(
+        `varPtrPtr` `(` $varPtrPtr `:` type($varPtrPtr) `)`
+      | `bounds` `(` $bounds `)`
+    ) `->` type($accPtr) attr-dict
+  }];
+
+  let hasVerifier = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// 2.7.4 deviceptr clause
+//===----------------------------------------------------------------------===//
+def OpenACC_DevicePtrOp : OpenACC_DataEntryOp<"deviceptr",
+    "mlir::acc::DataClause::acc_deviceptr"> {
+  let summary = "Specifies that the variable pointer is a device pointer.";
+}
+
+//===----------------------------------------------------------------------===//
+// 2.7.5 present clause
+//===----------------------------------------------------------------------===//
+def OpenACC_PresentOp : OpenACC_DataEntryOp<"present",
+    "mlir::acc::DataClause::acc_present"> {
+  let summary = "Specifies that the variable is already present on device.";
+}
+
+//===----------------------------------------------------------------------===//
+// 2.7.7 copyin clause
+//===----------------------------------------------------------------------===//
+def OpenACC_CopyinOp : OpenACC_DataEntryOp<"copyin",
+    "mlir::acc::DataClause::acc_copyin"> {
+  let summary = "Represents copyin semantics for acc data clauses like acc "
+                "copyin and acc copy.";
+
+  let extraClassDeclaration = [{
+    /// Check if this is a copyin with readonly modifier.
+    bool isCopyinReadonly();
+  }];
+}
+
+//===----------------------------------------------------------------------===//
+// 2.7.9 create clause
+//===----------------------------------------------------------------------===//
+def OpenACC_CreateOp : OpenACC_DataEntryOp<"create",
+    "mlir::acc::DataClause::acc_create"> {
+  let summary = "Represents create semantics for acc data clauses like acc "
+                "create and acc copyout.";
+
+  let extraClassDeclaration = [{
+    /// Check if this is a create with zero modifier.
+    bool isCreateZero();
+  }];
+}
+
+//===----------------------------------------------------------------------===//
+// 2.7.10 no_create clause
+//===----------------------------------------------------------------------===//
+def OpenACC_NoCreateOp : OpenACC_DataEntryOp<"nocreate",
+    "mlir::acc::DataClause::acc_no_create"> {
+  let summary = "Represents acc no_create semantics.";
+}
+
+//===----------------------------------------------------------------------===//
+// 2.7.12 attach clause
+//===----------------------------------------------------------------------===//
+def OpenACC_AttachOp : OpenACC_DataEntryOp<"attach",
+    "mlir::acc::DataClause::acc_attach"> {
+  let summary = "Represents acc attach semantics which updates a pointer in "
+                "device memory with the corresponding device address of the "
+                "pointee.";
+}
+
+//===----------------------------------------------------------------------===//
+// 3.2.23 acc_deviceptr
+//===----------------------------------------------------------------------===//
+// This is needed to get device address without the additional semantics in
+// acc present.
+// It is also useful for providing the device address for unstructured construct
+// exit_data since unlike structured constructs, there is no matching data entry
+// operation.
+def OpenACC_GetDevicePtrOp : OpenACC_DataEntryOp<"getdeviceptr",
+    "mlir::acc::DataClause::acc_getdeviceptr"> {
+  let summary = "Gets device address from host address if it exists on device.";
+}
+
+// Data exit operation does not refer to OpenACC spec terminology, but to
+// terminology used in this dialect. It refers to data operations that will appear
+// after data or compute region. It will be used as the base of acc dialect
+// operations for the following OpenACC data clauses: copyout, detach, delete.
+class OpenACC_DataExitOp<string mnemonic, string clause, list<Trait> traits = []> :
+    OpenACC_Op<mnemonic, !listconcat(traits,
+        [AttrSizedOperandSegments])> {
+  let arguments = (ins Optional<OpenACC_PointerLikeTypeInterface>:$varPtr,
+                       OpenACC_PointerLikeTypeInterface:$accPtr,
+                       Variadic<OpenACC_DataBoundsType>:$bounds,
+                       DefaultValuedAttr<OpenACC_DataClauseEnum,clause>:$dataClause,
+                       OptionalAttr<OpenACC_DataClauseEnum>:$decomposedFrom,
+                       DefaultValuedAttr<BoolAttr, "true">:$structured,
+                       DefaultValuedAttr<BoolAttr, "false">:$implicit,
+                       OptionalAttr<StrAttr>:$name);
+
+  let assemblyFormat = [{
+    `accPtr` `(` $accPtr `:` type($accPtr) `)`
+    oilist(
+        `bounds` `(` $bounds `)`
+      | `to` `varPtr` `(` $varPtr `:` type($varPtr) `)`
+    ) attr-dict
+  }];
+
+  let hasVerifier = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// 2.7.8 copyout clause
+//===----------------------------------------------------------------------===//
+def OpenACC_CopyoutOp : OpenACC_DataExitOp<"copyout",
+    "mlir::acc::DataClause::acc_copyout"> {
+  let summary = "Represents acc copyout semantics - reverse of copyin.";
+
+  let extraClassDeclaration = [{
+    /// Check if this is a copyout with zero modifier.
+    bool isCopyoutZero();
+  }];
+}
+
+//===----------------------------------------------------------------------===//
+// 2.7.11 delete clause
+//===----------------------------------------------------------------------===//
+def OpenACC_DeleteOp : OpenACC_DataExitOp<"delete",
+    "mlir::acc::DataClause::acc_delete"> {
+  let summary = "Represents acc delete semantics - reverse of create.";
+}
+
+//===----------------------------------------------------------------------===//
+// 2.7.13 detach clause
+//===----------------------------------------------------------------------===//
+def OpenACC_DetachOp : OpenACC_DataExitOp<"detach",
+    "mlir::acc::DataClause::acc_detach"> {
+  let summary = "Represents acc detach semantics - reverse of attach.";
+}
+
 //===----------------------------------------------------------------------===//
 // 2.5.1 parallel Construct
 //===----------------------------------------------------------------------===//
 
 def OpenACC_ParallelOp : OpenACC_Op<"parallel",
-    [AttrSizedOperandSegments]> {
+    [AttrSizedOperandSegments, RecursiveMemoryEffects]> {
   let summary = "parallel construct";
   let description = [{
     The "acc.parallel" operation represents a parallel construct block. It has
@@ -112,6 +333,7 @@ def OpenACC_ParallelOp : OpenACC_Op<"parallel",
                        Variadic<AnyType>:$attachOperands,
                        Variadic<AnyType>:$gangPrivateOperands,
                        Variadic<AnyType>:$gangFirstPrivateOperands,
+                       Variadic<OpenACC_PointerLikeTypeInterface>:$dataClauseOperands,
                        OptionalAttr<DefaultValueAttr>:$defaultAttr);
 
   let regions = (region AnyRegion:$region);
@@ -126,7 +348,8 @@ def OpenACC_ParallelOp : OpenACC_Op<"parallel",
 
   let assemblyFormat = [{
     oilist(
-        `attach` `(` $attachOperands `:` type($attachOperands) `)`
+        `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
+      | `attach` `(` $attachOperands `:` type($attachOperands) `)`
       | `async` `(` $async `:` type($async) `)`
       | `copy` `(` $copyOperands `:` type($copyOperands) `)`
       | `copyin` `(` $copyinOperands `:` type($copyinOperands) `)`
@@ -160,7 +383,8 @@ def OpenACC_ParallelOp : OpenACC_Op<"parallel",
 // 2.5.2 serial Construct
 //===----------------------------------------------------------------------===//
 
-def OpenACC_SerialOp : OpenACC_Op<"serial", [AttrSizedOperandSegments]> {
+def OpenACC_SerialOp : OpenACC_Op<"serial",
+    [AttrSizedOperandSegments, RecursiveMemoryEffects]> {
   let summary = "serial construct";
   let description = [{
     The "acc.serial" operation represents a serial construct block. It has
@@ -197,6 +421,7 @@ def OpenACC_SerialOp : OpenACC_Op<"serial", [AttrSizedOperandSegments]> {
                        Variadic<AnyType>:$attachOperands,
                        Variadic<AnyType>:$gangPrivateOperands,
                        Variadic<AnyType>:$gangFirstPrivateOperands,
+                       Variadic<OpenACC_PointerLikeTypeInterface>:$dataClauseOperands,
                        OptionalAttr<DefaultValueAttr>:$defaultAttr);
 
   let regions = (region AnyRegion:$region);
@@ -211,7 +436,8 @@ def OpenACC_SerialOp : OpenACC_Op<"serial", [AttrSizedOperandSegments]> {
 
   let assemblyFormat = [{
     oilist(
-        `attach` `(` $attachOperands `:` type($attachOperands) `)`
+        `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
+      | `attach` `(` $attachOperands `:` type($attachOperands) `)`
       | `async` `(` $async `:` type($async) `)`
       | `copy` `(` $copyOperands `:` type($copyOperands) `)`
       | `copyin` `(` $copyinOperands `:` type($copyinOperands) `)`
@@ -242,7 +468,8 @@ def OpenACC_SerialOp : OpenACC_Op<"serial", [AttrSizedOperandSegments]> {
 // 2.5.1 kernels Construct
 //===----------------------------------------------------------------------===//
 
-def OpenACC_KernelsOp : OpenACC_Op<"kernels", [AttrSizedOperandSegments]> {
+def OpenACC_KernelsOp : OpenACC_Op<"kernels",
+    [AttrSizedOperandSegments, RecursiveMemoryEffects]> {
   let summary = "kernels construct";
   let description = [{
     The "acc.kernels" operation represents a kernels construct block. It has
@@ -280,6 +507,7 @@ def OpenACC_KernelsOp : OpenACC_Op<"kernels", [AttrSizedOperandSegments]> {
                        Variadic<AnyType>:$presentOperands,
                        Variadic<AnyType>:$devicePtrOperands,
                        Variadic<AnyType>:$attachOperands,
+                       Variadic<OpenACC_PointerLikeTypeInterface>:$dataClauseOperands,
                        OptionalAttr<DefaultValueAttr>:$defaultAttr);
 
   let regions = (region AnyRegion:$region);
@@ -294,7 +522,8 @@ def OpenACC_KernelsOp : OpenACC_Op<"kernels", [AttrSizedOperandSegments]> {
 
   let assemblyFormat = [{
     oilist(
-        `attach` `(` $attachOperands `:` type($attachOperands) `)`
+        `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
+      | `attach` `(` $attachOperands `:` type($attachOperands) `)`
       | `async` `(` $async `:` type($async) `)`
       | `copy` `(` $copyOperands `:` type($copyOperands) `)`
       | `copyin` `(` $copyinOperands `:` type($copyinOperands) `)`
@@ -324,7 +553,7 @@ def OpenACC_KernelsOp : OpenACC_Op<"kernels", [AttrSizedOperandSegments]> {
 //===----------------------------------------------------------------------===//
 
 def OpenACC_DataOp : OpenACC_Op<"data",
-    [AttrSizedOperandSegments]> {
+    [AttrSizedOperandSegments, RecursiveMemoryEffects]> {
   let summary = "data construct";
 
   let description = [{
@@ -357,6 +586,7 @@ def OpenACC_DataOp : OpenACC_Op<"data",
                        Variadic<AnyType>:$presentOperands,
                        Variadic<AnyType>:$deviceptrOperands,
                        Variadic<AnyType>:$attachOperands,
+                       Variadic<OpenACC_PointerLikeTypeInterface>:$dataClauseOperands,
                        OptionalAttr<DefaultValueAttr>:$defaultAttr);
 
   let regions = (region AnyRegion:$region);
@@ -372,6 +602,7 @@ def OpenACC_DataOp : OpenACC_Op<"data",
   let assemblyFormat = [{
     oilist(
         `if` `(` $ifCond `)`
+      | `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
       | `copy` `(` $copyOperands `:` type($copyOperands) `)`
       | `copyin` `(` $copyinOperands `:` type($copyinOperands) `)`
       | `copyin_readonly` `(` $copyinReadonlyOperands `:`
@@ -431,7 +662,8 @@ def OpenACC_EnterDataOp : OpenACC_Op<"enter_data", [AttrSizedOperandSegments]> {
                        Variadic<AnyType>:$copyinOperands,
                        Variadic<AnyType>:$createOperands,
                        Variadic<AnyType>:$createZeroOperands,
-                       Variadic<AnyType>:$attachOperands);
+                       Variadic<AnyType>:$attachOperands,
+                       Variadic<OpenACC_PointerLikeTypeInterface>:$dataClauseOperands);
 
   let extraClassDeclaration = [{
     /// The number of data operands.
@@ -452,6 +684,7 @@ def OpenACC_EnterDataOp : OpenACC_Op<"enter_data", [AttrSizedOperandSegments]> {
       | `create_zero` `(` $createZeroOperands `:`
           type($createZeroOperands) `)`
       | `attach` `(` $attachOperands `:` type($attachOperands) `)`
+      | `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
     )
     attr-dict-with-keyword
   }];
@@ -486,6 +719,7 @@ def OpenACC_ExitDataOp : OpenACC_Op<"exit_data", [AttrSizedOperandSegments]> {
                        Variadic<AnyType>:$copyoutOperands,
                        Variadic<AnyType>:$deleteOperands,
                        Variadic<AnyType>:$detachOperands,
+                       Variadic<OpenACC_PointerLikeTypeInterface>:$dataClauseOperands,
                        UnitAttr:$finalize);
 
   let extraClassDeclaration = [{
@@ -505,6 +739,7 @@ def OpenACC_ExitDataOp : OpenACC_Op<"exit_data", [AttrSizedOperandSegments]> {
       | `copyout` `(` $copyoutOperands `:` type($copyoutOperands) `)`
       | `delete` `(` $deleteOperands `:` type($deleteOperands) `)`
       | `detach` `(` $detachOperands `:` type($detachOperands) `)`
+      | `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
     )
     attr-dict-with-keyword
   }];
@@ -517,7 +752,8 @@ def OpenACC_ExitDataOp : OpenACC_Op<"exit_data", [AttrSizedOperandSegments]> {
 // 2.9 loop Construct
 //===----------------------------------------------------------------------===//
 
-def OpenACC_LoopOp : OpenACC_Op<"loop", [AttrSizedOperandSegments]> {
+def OpenACC_LoopOp : OpenACC_Op<"loop",
+    [AttrSizedOperandSegments, RecursiveMemoryEffects]> {
   let summary = "loop construct";
 
   let description = [{
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOpsTypes.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOpsTypes.td
new file mode 100644 (file)
index 0000000..41a18e4
--- /dev/null
@@ -0,0 +1,27 @@
+//===- OpenACCOpsTypes.td - OpenACC operation types definitions -*- tablegen -*-===//
+//
+// Part of the MLIR Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// =============================================================================
+//
+// Defines MLIR OpenACC operation types.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef OPENACC_OPS_TYPES
+#define OPENACC_OPS_TYPES
+
+include "mlir/IR/AttrTypeBase.td"
+include "OpenACCBase.td"
+
+class OpenACC_Type<string name, string typeMnemonic> : TypeDef<OpenACC_Dialect, name> {
+  let mnemonic = typeMnemonic;
+}
+
+def OpenACC_DataBoundsType : OpenACC_Type<"DataBounds", "data_bounds_ty"> {
+  let summary = "Type for representing acc data clause bounds information";
+}
+
+#endif // OPENACC_OPS_TYPES
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td
new file mode 100644 (file)
index 0000000..0a3edd5
--- /dev/null
@@ -0,0 +1,34 @@
+//===-- OpenACCTypeInterfaces.td - OpenACC type interfaces ---*- tablegen -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef OPENACC_TYPE_INTERFACES
+#define OPENACC_TYPE_INTERFACES
+
+include "mlir/IR/OpBase.td"
+
+def OpenACC_PointerLikeTypeInterface : TypeInterface<"PointerLikeType"> {
+  let cppNamespace = "::mlir::acc";
+
+  let description = [{
+    An interface for pointer-like types that point to an OpenACC var.
+  }];
+
+  // By convention, any of the pointer types associated with this interface
+  // will need to provide getElementType.
+  let methods = [
+    InterfaceMethod<
+      /*description=*/[{
+        Returns the pointee type or null if the pointer has no pointee type
+      }],
+      /*retTy=*/"::mlir::Type",
+      /*methodName=*/"getElementType"
+    >,
+  ];
+}
+
+#endif // OPENACC_TYPE_INTERFACES
index 770623c..5e09044 100644 (file)
@@ -6,6 +6,9 @@ add_mlir_dialect_library(MLIROpenACCDialect
 
   DEPENDS
   MLIROpenACCOpsIncGen
+  MLIROpenACCEnumsIncGen
+  MLIROpenACCAttributesIncGen
+  MLIROpenACCTypeInterfacesIncGen
 
   LINK_LIBS PUBLIC
   MLIRIR
index 4b3b438..17f1bbd 100644 (file)
@@ -7,7 +7,9 @@
 // =============================================================================
 
 #include "mlir/Dialect/OpenACC/OpenACC.h"
-#include "mlir/Dialect/OpenACC/OpenACCOpsEnums.cpp.inc"
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
+#include "mlir/Dialect/MemRef/IR/MemRef.h"
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/DialectImplementation.h"
@@ -20,6 +22,8 @@ using namespace mlir;
 using namespace acc;
 
 #include "mlir/Dialect/OpenACC/OpenACCOpsDialect.cpp.inc"
+#include "mlir/Dialect/OpenACC/OpenACCOpsEnums.cpp.inc"
+#include "mlir/Dialect/OpenACC/OpenACCTypeInterfaces.cpp.inc"
 
 //===----------------------------------------------------------------------===//
 // OpenACC operations
@@ -34,6 +38,161 @@ void OpenACCDialect::initialize() {
 #define GET_ATTRDEF_LIST
 #include "mlir/Dialect/OpenACC/OpenACCOpsAttributes.cpp.inc"
       >();
+  addTypes<
+#define GET_TYPEDEF_LIST
+#include "mlir/Dialect/OpenACC/OpenACCOpsTypes.cpp.inc"
+      >();
+
+  // By attaching interfaces here, we make the OpenACC dialect dependent on
+  // the other dialects. This is probably better than having dialects like LLVM
+  // and memref be dependent on OpenACC.
+  LLVM::LLVMPointerType::attachInterface<PointerLikeType>(*getContext());
+  MemRefType::attachInterface<PointerLikeType>(*getContext());
+}
+
+//===----------------------------------------------------------------------===//
+// DataBoundsOp
+//===----------------------------------------------------------------------===//
+LogicalResult acc::DataBoundsOp::verify() {
+  auto extent = getExtent();
+  auto upperbound = getUpperbound();
+  if (!extent && !upperbound) {
+    return emitError("expected extent or upperbound.");
+  }
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// DevicePtrOp
+//===----------------------------------------------------------------------===//
+LogicalResult acc::DevicePtrOp::verify() {
+  if (getDataClause() != acc::DataClause::acc_deviceptr) {
+    return emitError("data clause associated with deviceptr operation must "
+                     "match its intent");
+  }
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// PresentOp
+//===----------------------------------------------------------------------===//
+LogicalResult acc::PresentOp::verify() {
+  if (getDataClause() != acc::DataClause::acc_present) {
+    return emitError(
+        "data clause associated with present operation must match its intent");
+  }
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// CopyinOp
+//===----------------------------------------------------------------------===//
+LogicalResult acc::CopyinOp::verify() {
+  if (getDataClause() != acc::DataClause::acc_copyin &&
+      getDataClause() != acc::DataClause::acc_copyin_readonly) {
+    return emitError(
+        "data clause associated with copyin operation must match its intent");
+  }
+  return success();
+}
+
+bool acc::CopyinOp::isCopyinReadonly() {
+  return getDataClause() == acc::DataClause::acc_copyin_readonly;
+}
+
+//===----------------------------------------------------------------------===//
+// CreateOp
+//===----------------------------------------------------------------------===//
+LogicalResult acc::CreateOp::verify() {
+  if (getDataClause() != acc::DataClause::acc_create &&
+      getDataClause() != acc::DataClause::acc_create_zero) {
+    return emitError(
+        "data clause associated with create operation must match its intent");
+  }
+  return success();
+}
+
+bool acc::CreateOp::isCreateZero() {
+  return getDataClause() == acc::DataClause::acc_create_zero;
+}
+
+//===----------------------------------------------------------------------===//
+// NoCreateOp
+//===----------------------------------------------------------------------===//
+LogicalResult acc::NoCreateOp::verify() {
+  if (getDataClause() != acc::DataClause::acc_no_create) {
+    return emitError("data clause associated with no_create operation must "
+                     "match its intent");
+  }
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// AttachOp
+//===----------------------------------------------------------------------===//
+LogicalResult acc::AttachOp::verify() {
+  if (getDataClause() != acc::DataClause::acc_attach) {
+    return emitError(
+        "data clause associated with attach operation must match its intent");
+  }
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// GetDevicePtrOp
+//===----------------------------------------------------------------------===//
+LogicalResult acc::GetDevicePtrOp::verify() {
+  if (getDataClause() != acc::DataClause::acc_getdeviceptr) {
+    return emitError("getDevicePtr mismatch");
+  }
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// CopyoutOp
+//===----------------------------------------------------------------------===//
+LogicalResult acc::CopyoutOp::verify() {
+  if (getDataClause() != acc::DataClause::acc_copyout &&
+      getDataClause() != acc::DataClause::acc_copyout_zero) {
+    return emitError(
+        "data clause associated with copyout operation must match its intent");
+  }
+  if (!getVarPtr() || !getAccPtr()) {
+    return emitError("must have both host and device pointers");
+  }
+  return success();
+}
+
+bool acc::CopyoutOp::isCopyoutZero() {
+  return getDataClause() == acc::DataClause::acc_copyout_zero;
+}
+
+//===----------------------------------------------------------------------===//
+// DeleteOp
+//===----------------------------------------------------------------------===//
+LogicalResult acc::DeleteOp::verify() {
+  if (getDataClause() != acc::DataClause::acc_delete) {
+    return emitError(
+        "data clause associated with delete operation must match its intent");
+  }
+  if (!getVarPtr() && !getAccPtr()) {
+    return emitError("must have either host or device pointer");
+  }
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// DetachOp
+//===----------------------------------------------------------------------===//
+LogicalResult acc::DetachOp::verify() {
+  if (getDataClause() != acc::DataClause::acc_detach) {
+    return emitError(
+        "data clause associated with detach operation must match its intent");
+  }
+  if (!getVarPtr() && !getAccPtr()) {
+    return emitError("must have either host or device pointer");
+  }
+  return success();
 }
 
 template <typename StructureOp>
@@ -95,7 +254,8 @@ unsigned ParallelOp::getNumDataOperands() {
          getCreateOperands().size() + getCreateZeroOperands().size() +
          getNoCreateOperands().size() + getPresentOperands().size() +
          getDevicePtrOperands().size() + getAttachOperands().size() +
-         getGangPrivateOperands().size() + getGangFirstPrivateOperands().size();
+         getGangPrivateOperands().size() +
+         getGangFirstPrivateOperands().size() + getDataClauseOperands().size();
 }
 
 Value ParallelOp::getDataOperand(unsigned i) {
@@ -119,7 +279,8 @@ unsigned SerialOp::getNumDataOperands() {
          getCreateOperands().size() + getCreateZeroOperands().size() +
          getNoCreateOperands().size() + getPresentOperands().size() +
          getDevicePtrOperands().size() + getAttachOperands().size() +
-         getGangPrivateOperands().size() + getGangFirstPrivateOperands().size();
+         getGangPrivateOperands().size() +
+         getGangFirstPrivateOperands().size() + getDataClauseOperands().size();
 }
 
 Value SerialOp::getDataOperand(unsigned i) {
@@ -139,7 +300,7 @@ unsigned KernelsOp::getNumDataOperands() {
          getCopyoutZeroOperands().size() + getCreateOperands().size() +
          getCreateZeroOperands().size() + getNoCreateOperands().size() +
          getPresentOperands().size() + getDevicePtrOperands().size() +
-         getAttachOperands().size();
+         getAttachOperands().size() + getDataClauseOperands().size();
 }
 
 Value KernelsOp::getDataOperand(unsigned i) {
@@ -290,7 +451,7 @@ unsigned DataOp::getNumDataOperands() {
          getCopyoutZeroOperands().size() + getCreateOperands().size() +
          getCreateZeroOperands().size() + getNoCreateOperands().size() +
          getPresentOperands().size() + getDeviceptrOperands().size() +
-         getAttachOperands().size();
+         getAttachOperands().size() + getDataClauseOperands().size();
 }
 
 Value DataOp::getDataOperand(unsigned i) {
@@ -307,7 +468,7 @@ LogicalResult acc::ExitDataOp::verify() {
   // At least one copyout, delete, or detach clause must appear on an exit data
   // directive.
   if (getCopyoutOperands().empty() && getDeleteOperands().empty() &&
-      getDetachOperands().empty())
+      getDetachOperands().empty() && getDataClauseOperands().empty())
     return emitError(
         "at least one operand in copyout, delete or detach must appear on the "
         "exit data operation");
@@ -330,7 +491,7 @@ LogicalResult acc::ExitDataOp::verify() {
 
 unsigned ExitDataOp::getNumDataOperands() {
   return getCopyoutOperands().size() + getDeleteOperands().size() +
-         getDetachOperands().size();
+         getDetachOperands().size() + getDataClauseOperands().size();
 }
 
 Value ExitDataOp::getDataOperand(unsigned i) {
@@ -354,7 +515,8 @@ LogicalResult acc::EnterDataOp::verify() {
   // At least one copyin, create, or attach clause must appear on an enter data
   // directive.
   if (getCopyinOperands().empty() && getCreateOperands().empty() &&
-      getCreateZeroOperands().empty() && getAttachOperands().empty())
+      getCreateZeroOperands().empty() && getAttachOperands().empty() &&
+      getDataClauseOperands().empty())
     return emitError(
         "at least one operand in copyin, create, "
         "create_zero or attach must appear on the enter data operation");
@@ -377,7 +539,8 @@ LogicalResult acc::EnterDataOp::verify() {
 
 unsigned EnterDataOp::getNumDataOperands() {
   return getCopyinOperands().size() + getCreateOperands().size() +
-         getCreateZeroOperands().size() + getAttachOperands().size();
+         getCreateZeroOperands().size() + getAttachOperands().size() +
+         getDataClauseOperands().size();
 }
 
 Value EnterDataOp::getDataOperand(unsigned i) {
@@ -480,3 +643,6 @@ LogicalResult acc::WaitOp::verify() {
 
 #define GET_ATTRDEF_CLASSES
 #include "mlir/Dialect/OpenACC/OpenACCOpsAttributes.cpp.inc"
+
+#define GET_TYPEDEF_CLASSES
+#include "mlir/Dialect/OpenACC/OpenACCOpsTypes.cpp.inc"
index b1ee051..825bc06 100644 (file)
@@ -974,3 +974,145 @@ func.func @testenterdataop(%a: memref<10xf32>, %b: memref<10xf32>, %c: memref<10
 // CHECK: acc.enter_data async([[I64VALUE]] : i64) copyin([[ARGA]] : memref<10xf32>)
 // CHECK: acc.enter_data if([[IFCOND]]) copyin([[ARGA]] : memref<10xf32>)
 // CHECK: acc.enter_data wait_devnum([[I64VALUE]] : i64) wait([[I32VALUE]], [[IDXVALUE]] : i32, index) copyin([[ARGA]] : memref<10xf32>)
+
+// -----
+
+func.func @teststructureddataclauseops(%a: memref<10xf32>, %b: memref<memref<10xf32>>, %c: memref<10x20xf32>) -> () {
+  %deviceptr = acc.deviceptr varPtr(%a : memref<10xf32>) -> memref<10xf32> {name = "arrayA"}
+  acc.parallel dataOperands(%deviceptr : memref<10xf32>) {
+  }
+
+  %present = acc.present varPtr(%a : memref<10xf32>) -> memref<10xf32>
+  acc.data dataOperands(%present : memref<10xf32>) {
+  }
+
+  %copyin = acc.copyin varPtr(%a : memref<10xf32>) -> memref<10xf32>
+  acc.parallel dataOperands(%copyin : memref<10xf32>) {
+  }
+
+  %copyinreadonly = acc.copyin varPtr(%a : memref<10xf32>) -> memref<10xf32> {dataClause = 2}
+  acc.kernels dataOperands(%copyinreadonly : memref<10xf32>) {
+  }
+
+  %copyinfromcopy = acc.copyin varPtr(%a : memref<10xf32>) -> memref<10xf32> {decomposedFrom = 3}
+  acc.serial dataOperands(%copyinfromcopy : memref<10xf32>) {
+  }
+  acc.copyout accPtr(%copyinfromcopy : memref<10xf32>) to varPtr(%a : memref<10xf32>) {decomposedFrom = 3}
+
+  %create = acc.create varPtr(%a : memref<10xf32>) -> memref<10xf32>
+  %createimplicit = acc.create varPtr(%c : memref<10x20xf32>) -> memref<10x20xf32> {implicit = true}
+  acc.parallel dataOperands(%create, %createimplicit : memref<10xf32>, memref<10x20xf32>) {
+  }
+  acc.delete accPtr(%create : memref<10xf32>) {decomposedFrom = 7}
+  acc.delete accPtr(%createimplicit : memref<10x20xf32>) {decomposedFrom = 7, implicit = true}
+
+  %copyoutzero = acc.create varPtr(%a : memref<10xf32>) -> memref<10xf32> {decomposedFrom = 5}
+  acc.parallel dataOperands(%copyoutzero: memref<10xf32>) {
+  }
+  acc.copyout accPtr(%copyoutzero : memref<10xf32>) to varPtr(%a : memref<10xf32>) {dataClause = 5}
+
+  %attach = acc.attach varPtr(%b : memref<memref<10xf32>>) -> memref<memref<10xf32>>
+  acc.parallel dataOperands(%attach : memref<memref<10xf32>>) {
+  }
+  acc.detach accPtr(%attach : memref<memref<10xf32>>) {decomposedFrom = 10}
+
+  %copyinparent = acc.copyin varPtr(%a : memref<10xf32>) varPtrPtr(%b : memref<memref<10xf32>>) -> memref<10xf32> {decomposedFrom = 3}
+  acc.parallel dataOperands(%copyinparent : memref<10xf32>) {
+  }
+  acc.copyout accPtr(%copyinparent : memref<10xf32>) to varPtr(%a : memref<10xf32>) {decomposedFrom = 3}
+
+  %c0 = arith.constant 0 : index
+  %c1 = arith.constant 1 : index
+  %c4 = arith.constant 4 : index
+  %c9 = arith.constant 9 : index
+  %c10 = arith.constant 10 : index
+  %c20 = arith.constant 20 : index
+
+  %bounds1full = acc.bounds lowerbound(%c0 : index) upperbound(%c9 : index) stride(%c1 : index)
+  %copyinfullslice1 = acc.copyin varPtr(%a : memref<10xf32>) bounds(%bounds1full) -> memref<10xf32> {name = "arrayA[0:9]"}
+  // Specify full-bounds but assume that startIdx of array reference is 1.
+  %bounds2full = acc.bounds lowerbound(%c1 : index) upperbound(%c20 : index) extent(%c20 : index) stride(%c4 : index) startIdx(%c1 : index) {strideInBytes = true}
+  %copyinfullslice2 = acc.copyin varPtr(%c : memref<10x20xf32>) bounds(%bounds1full, %bounds2full) -> memref<10x20xf32>
+  acc.parallel dataOperands(%copyinfullslice1, %copyinfullslice2 : memref<10xf32>, memref<10x20xf32>) {
+  }
+
+  %bounds1partial = acc.bounds lowerbound(%c4 : index) upperbound(%c9 : index) stride(%c1 : index)
+  %copyinpartial = acc.copyin varPtr(%a : memref<10xf32>) bounds(%bounds1partial) -> memref<10xf32> {decomposedFrom = 3}
+  acc.parallel dataOperands(%copyinpartial : memref<10xf32>) {
+  }
+  acc.copyout accPtr(%copyinpartial : memref<10xf32>) bounds(%bounds1partial) to varPtr(%a : memref<10xf32>) {decomposedFrom = 3}
+
+  return
+}
+
+// CHECK: func.func @teststructureddataclauseops([[ARGA:%.*]]: memref<10xf32>, [[ARGB:%.*]]: memref<memref<10xf32>>, [[ARGC:%.*]]: memref<10x20xf32>) {
+// CHECK: [[DEVICEPTR:%.*]] = acc.deviceptr varPtr([[ARGA]] : memref<10xf32>) -> memref<10xf32> {name = "arrayA"}
+// CHECK-NEXT: acc.parallel dataOperands([[DEVICEPTR]] : memref<10xf32>) {
+// CHECK-NEXT: }
+// CHECK: [[PRESENT:%.*]] = acc.present varPtr([[ARGA]] : memref<10xf32>) -> memref<10xf32>
+// CHECK-NEXT: acc.data dataOperands([[PRESENT]] : memref<10xf32>) {
+// CHECK-NEXT: }
+// CHECK: [[COPYIN:%.*]] = acc.copyin varPtr([[ARGA]] : memref<10xf32>) -> memref<10xf32>
+// CHECK-NEXT: acc.parallel dataOperands([[COPYIN]] : memref<10xf32>) {
+// CHECK-NEXT: }
+// CHECK: [[COPYINRO:%.*]] = acc.copyin varPtr([[ARGA]] : memref<10xf32>) -> memref<10xf32> {dataClause = 2 : i64}
+// CHECK-NEXT: acc.kernels dataOperands([[COPYINRO]] : memref<10xf32>) {
+// CHECK-NEXT: }
+// CHECK: [[COPYINCOPY:%.*]] = acc.copyin varPtr([[ARGA]] : memref<10xf32>) -> memref<10xf32> {decomposedFrom = 3 : i64}
+// CHECK-NEXT: acc.serial dataOperands([[COPYINCOPY]] : memref<10xf32>) {
+// CHECK-NEXT: }
+// CHECK-NEXT: acc.copyout accPtr([[COPYINCOPY]] : memref<10xf32>) to varPtr([[ARGA]] : memref<10xf32>) {decomposedFrom = 3 : i64}
+// CHECK: [[CREATE:%.*]] = acc.create varPtr([[ARGA]] : memref<10xf32>) -> memref<10xf32>
+// CHECK-NEXT: [[CREATEIMP:%.*]] = acc.create varPtr([[ARGC]] : memref<10x20xf32>) -> memref<10x20xf32> {implicit = true}
+// CHECK-NEXT: acc.parallel dataOperands([[CREATE]], [[CREATEIMP]] : memref<10xf32>, memref<10x20xf32>) {
+// CHECK-NEXT: }
+// CHECK-NEXT: acc.delete accPtr([[CREATE]] : memref<10xf32>) {decomposedFrom = 7 : i64}
+// CHECK-NEXT: acc.delete accPtr([[CREATEIMP]] : memref<10x20xf32>) {decomposedFrom = 7 : i64, implicit = true}
+// CHECK: [[COPYOUTZ:%.*]] = acc.create varPtr([[ARGA]] : memref<10xf32>) -> memref<10xf32> {decomposedFrom = 5 : i64}
+// CHECK-NEXT: acc.parallel dataOperands([[COPYOUTZ]] : memref<10xf32>) {
+// CHECK-NEXT: }
+// CHECK-NEXT: acc.copyout accPtr([[COPYOUTZ]] : memref<10xf32>) to varPtr([[ARGA]] : memref<10xf32>) {dataClause = 5 : i64}
+// CHECK: [[ATTACH:%.*]] = acc.attach varPtr([[ARGB]] : memref<memref<10xf32>>) -> memref<memref<10xf32>>
+// CHECK-NEXT: acc.parallel dataOperands([[ATTACH]] : memref<memref<10xf32>>) {
+// CHECK-NEXT: }
+// CHECK-NEXT: acc.detach accPtr([[ATTACH]] : memref<memref<10xf32>>) {decomposedFrom = 10 : i64}
+// CHECK: [[COPYINP:%.*]] = acc.copyin varPtr([[ARGA]] : memref<10xf32>) varPtrPtr([[ARGB]] : memref<memref<10xf32>>) -> memref<10xf32> {decomposedFrom = 3 : i64}
+// CHECK-NEXT: acc.parallel dataOperands([[COPYINP]] : memref<10xf32>) {
+// CHECK-NEXT: }
+// CHECK-NEXT: acc.copyout accPtr([[COPYINP]] : memref<10xf32>) to varPtr([[ARGA]] : memref<10xf32>) {decomposedFrom = 3 : i64}
+// CHECK-DAG: [[CON0:%.*]] = arith.constant 0 : index
+// CHECK-DAG: [[CON1:%.*]] = arith.constant 1 : index
+// CHECK-DAG: [[CON4:%.*]] = arith.constant 4 : index
+// CHECK-DAG: [[CON9:%.*]] = arith.constant 9 : index
+// CHECK-DAG: [[CON20:%.*]] = arith.constant 20 : index
+// CHECK: [[BOUNDS1F:%.*]] = acc.bounds lowerbound([[CON0]] : index) upperbound([[CON9]] : index) stride([[CON1]] : index)
+// CHECK-NEXT: [[COPYINF1:%.*]] = acc.copyin varPtr([[ARGA]] : memref<10xf32>) bounds([[BOUNDS1F]]) -> memref<10xf32> {name = "arrayA[0:9]"}
+// CHECK-NEXT: [[BOUNDS2F:%.*]] = acc.bounds lowerbound([[CON1]] : index) upperbound([[CON20]] : index) extent([[CON20]] : index) stride([[CON4]] : index) startIdx([[CON1]] : index) {strideInBytes = true}
+// CHECK-NEXT: [[COPYINF2:%.*]] = acc.copyin varPtr([[ARGC]] : memref<10x20xf32>) bounds([[BOUNDS1F]], [[BOUNDS2F]]) -> memref<10x20xf32>
+// CHECK-NEXT: acc.parallel dataOperands([[COPYINF1]], [[COPYINF2]] : memref<10xf32>, memref<10x20xf32>) {
+// CHECK-NEXT: }
+// CHECK: [[BOUNDS1P:%.*]] = acc.bounds lowerbound([[CON4]] : index) upperbound([[CON9]] : index) stride([[CON1]] : index)
+// CHECK-NEXT: [[COPYINPART:%.*]] = acc.copyin varPtr([[ARGA]] : memref<10xf32>) bounds([[BOUNDS1P]]) -> memref<10xf32> {decomposedFrom = 3 : i64}
+// CHECK-NEXT: acc.parallel dataOperands([[COPYINPART]] : memref<10xf32>) {
+// CHECK-NEXT: }
+// CHECK-NEXT: acc.copyout accPtr([[COPYINPART]] : memref<10xf32>) bounds([[BOUNDS1P]]) to varPtr([[ARGA]] : memref<10xf32>) {decomposedFrom = 3 : i64}
+
+// -----
+
+func.func @testunstructuredclauseops(%a: memref<10xf32>) -> () {
+  %copyin = acc.copyin varPtr(%a : memref<10xf32>) -> memref<10xf32> {structured = false}
+  acc.enter_data dataOperands(%copyin : memref<10xf32>)
+
+  %devptr = acc.getdeviceptr varPtr(%a : memref<10xf32>) -> memref<10xf32> {decomposedFrom = 4}
+  acc.exit_data dataOperands(%devptr : memref<10xf32>)
+  acc.copyout accPtr(%devptr : memref<10xf32>) to varPtr(%a : memref<10xf32>) {structured = false}
+
+  return
+}
+
+// CHECK: func.func @testunstructuredclauseops([[ARGA:%.*]]: memref<10xf32>) {
+// CHECK: [[COPYIN:%.*]] = acc.copyin varPtr([[ARGA]] : memref<10xf32>) -> memref<10xf32> {structured = false}
+// CHECK-NEXT: acc.enter_data dataOperands([[COPYIN]] : memref<10xf32>)
+// CHECK: [[DEVPTR:%.*]] = acc.getdeviceptr varPtr([[ARGA]] : memref<10xf32>) -> memref<10xf32> {decomposedFrom = 4 : i64}
+// CHECK-NEXT: acc.exit_data dataOperands([[DEVPTR]] : memref<10xf32>)
+// CHECK-NEXT: acc.copyout accPtr([[DEVPTR]] : memref<10xf32>) to varPtr([[ARGA]] : memref<10xf32>) {structured = false}