diff --git a/mlir/include/mlir/Dialect/OpenACC/CMakeLists.txt b/mlir/include/mlir/Dialect/OpenACC/CMakeLists.txt --- a/mlir/include/mlir/Dialect/OpenACC/CMakeLists.txt +++ b/mlir/include/mlir/Dialect/OpenACC/CMakeLists.txt @@ -2,15 +2,25 @@ 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) 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 @@ -13,11 +13,17 @@ #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 --- /dev/null +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCBase.td @@ -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 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 @@ -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. @@ -13,21 +13,13 @@ #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,238 @@ // Type used in operation below. def IntOrIndex : AnyTypeOf<[AnyInteger, Index]>; +// Simple alias to pointer-like interface to reduce verbosity. +def OpenACC_PointerLikeType : TypeAlias; + +// Define the OpenACC data operations. 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 operation into multiple acc +// dialect operations, we need to keep track of original operation. Thus even +// for the operations with modifier, we create separate operation to make this +// possible. +def OpenACC_NoOp : I64EnumAttrCase<"acc_noop", 0>; +def OpenACC_CopyinOp : I64EnumAttrCase<"acc_copyin", 1>; +def OpenACC_CopyinReadonlyOp : I64EnumAttrCase<"acc_copyin_readonly", 2>; +def OpenACC_CopyOp : I64EnumAttrCase<"acc_copy", 3>; +def OpenACC_CopyoutOp : I64EnumAttrCase<"acc_copyout", 4>; +def OpenACC_CopyoutZeroOp : I64EnumAttrCase<"acc_copyout_zero", 5>; +def OpenACC_PresentOp : I64EnumAttrCase<"acc_present", 6>; +def OpenACC_CreateOp : I64EnumAttrCase<"acc_create", 7>; +def OpenACC_CreateZeroOp : I64EnumAttrCase<"acc_create_zero", 8>; +def OpenACC_DeleteOp : I64EnumAttrCase<"acc_delete", 9>; +def OpenACC_AttachOp : I64EnumAttrCase<"acc_attach", 10>; +def OpenACC_DetachOp : I64EnumAttrCase<"acc_detach", 11>; +def OpenACC_NoCreateOp : I64EnumAttrCase<"acc_no_create", 12>; +def OpenACC_PrivateOp : I64EnumAttrCase<"acc_private", 13>; +def OpenACC_FirstPrivateOp : I64EnumAttrCase<"acc_firstprivate", 14>; +def OpenACC_IsDevicePtrOp : I64EnumAttrCase<"acc_deviceptr", 15>; +def OpenACC_GetDevicePtrOp : I64EnumAttrCase<"acc_getdeviceptr", 16>; + +def OpenACC_DataClauseEnum : I64EnumAttr<"DataClause", + "data clauses supported by OpenACC", + [OpenACC_NoOp, OpenACC_CopyinOp, OpenACC_CopyinReadonlyOp, OpenACC_CopyOp, + OpenACC_CopyoutOp, OpenACC_CopyoutZeroOp, OpenACC_PresentOp, + OpenACC_CreateOp, OpenACC_CreateZeroOp, OpenACC_DeleteOp, + OpenACC_AttachOp, OpenACC_DetachOp, OpenACC_NoCreateOp, + OpenACC_PrivateOp, OpenACC_FirstPrivateOp, OpenACC_IsDevicePtrOp, + OpenACC_GetDevicePtrOp + ]> { + 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_DataBounds : OpenACC_Op<"bounds", [AttrSizedOperandSegments, NoMemoryEffect]> { + let summary = "Represents bounds information for acc data clause."; + + let arguments = (ins Optional:$lowerbound, + Optional:$upperbound, + Optional:$extent, + Optional:$stride, + DefaultValuedAttr:$strideInBytes, + Optional:$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 clause 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. +// +// Memory Effects are not yet modeled in the best way - we have both host and device +// effects. Since there is no ssa-modeled dependency between memory operations, +// for now mark all operations as reading so they cannot be reordered across +// acc regions. Since the data entry operations also produce a memory reference, +// any writes to that memory will be explicitly seen in the region it is used in. +// Hence why we don't have to model it as write from the mapping operation standpoint. +class OpenACC_DataEntryClause traits = []> : + OpenACC_Op])> { + let arguments = (ins OpenACC_PointerLikeTypeInterface:$varPtr, + Optional:$varPtrPtr, + Variadic:$bounds, + DefaultValuedAttr:$dataClause, + OptionalAttr:$decomposedFrom, + DefaultValuedAttr:$structured, + DefaultValuedAttr:$implicit, + OptionalAttr:$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_DevicePtrClause : OpenACC_DataEntryClause<"deviceptr", + "mlir::acc::DataClause::acc_deviceptr"> { + let summary = "Specifies that the variable pointer is a device pointer."; +} + +//===----------------------------------------------------------------------===// +// 2.7.5 present clause +//===----------------------------------------------------------------------===// +def OpenACC_PresentClause : OpenACC_DataEntryClause<"present", + "mlir::acc::DataClause::acc_present"> { + let summary = "Specifies that the variable is already present on device."; +} + +//===----------------------------------------------------------------------===// +// 2.7.7 copyin clause +//===----------------------------------------------------------------------===// +def OpenACC_CopyinClause : OpenACC_DataEntryClause<"copyin", + "mlir::acc::DataClause::acc_copyin"> { + let summary = "Represents copyin semantics for acc data clauses like acc copyin and acc copy."; + + let extraClassDeclaration = [{ + /// The number of data operands. + bool isCopyinReadonly(); + }]; +} + +//===----------------------------------------------------------------------===// +// 2.7.9 create clause +//===----------------------------------------------------------------------===// +def OpenACC_CreateClause : OpenACC_DataEntryClause<"create", + "mlir::acc::DataClause::acc_create"> { + let summary = "Represents create semantics for acc data clauses like acc create and acc copyout."; + + let extraClassDeclaration = [{ + /// The number of data operands. + bool isCreateZero(); + }]; +} + +//===----------------------------------------------------------------------===// +// 2.7.10 no_create clause +//===----------------------------------------------------------------------===// +def OpenACC_NoCreateClause : OpenACC_DataEntryClause<"nocreate", + "mlir::acc::DataClause::acc_no_create"> { + let summary = "Represents acc no_create semantics."; +} + +//===----------------------------------------------------------------------===// +// 2.7.12 attach clause +//===----------------------------------------------------------------------===// +def OpenACC_AttachClause : OpenACC_DataEntryClause<"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_GetDevicePtr : OpenACC_DataEntryClause<"getdeviceptr", + "mlir::acc::DataClause::acc_getdeviceptr"> { + let summary = "Gets device address from host address if it exists on device."; +} + +// Data exit clause 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_DataExitClause traits = []> : + OpenACC_Op { + let arguments = (ins Optional:$varPtr, + OpenACC_PointerLikeTypeInterface:$accPtr, + Variadic:$bounds, + DefaultValuedAttr:$dataClause, + OptionalAttr:$decomposedFrom, + DefaultValuedAttr:$structured, + DefaultValuedAttr:$implicit, + OptionalAttr:$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_CopyoutClause : OpenACC_DataExitClause<"copyout", + "mlir::acc::DataClause::acc_copyout", [MemoryEffects<[MemWrite]>]> { + let summary = "Represents acc copyout semantics - reverse of copyin."; + + let extraClassDeclaration = [{ + /// The number of data operands. + bool isCopyoutZero(); + }]; +} + +//===----------------------------------------------------------------------===// +// 2.7.11 delete clause +//===----------------------------------------------------------------------===// +def OpenACC_DeleteClause : OpenACC_DataExitClause<"delete", + "mlir::acc::DataClause::acc_delete", [MemoryEffects<[MemRead]>]> { + let summary = "Represents acc delete semantics - reverse of create."; +} + +//===----------------------------------------------------------------------===// +// 2.7.13 detach clause +//===----------------------------------------------------------------------===// +def OpenACC_DetachClause : OpenACC_DataExitClause<"detach", + "mlir::acc::DataClause::acc_detach", [MemoryEffects<[MemRead]>]> { + 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 +330,7 @@ Variadic:$attachOperands, Variadic:$gangPrivateOperands, Variadic:$gangFirstPrivateOperands, + Variadic:$dataClauseOperands, OptionalAttr:$defaultAttr); let regions = (region AnyRegion:$region); @@ -126,7 +345,8 @@ 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 +380,8 @@ // 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 +418,7 @@ Variadic:$attachOperands, Variadic:$gangPrivateOperands, Variadic:$gangFirstPrivateOperands, + Variadic:$dataClauseOperands, OptionalAttr:$defaultAttr); let regions = (region AnyRegion:$region); @@ -211,7 +433,8 @@ 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 +465,8 @@ // 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 +504,7 @@ Variadic:$presentOperands, Variadic:$devicePtrOperands, Variadic:$attachOperands, + Variadic:$dataClauseOperands, OptionalAttr:$defaultAttr); let regions = (region AnyRegion:$region); @@ -294,7 +519,8 @@ 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 +550,7 @@ //===----------------------------------------------------------------------===// def OpenACC_DataOp : OpenACC_Op<"data", - [AttrSizedOperandSegments]> { + [AttrSizedOperandSegments, RecursiveMemoryEffects]> { let summary = "data construct"; let description = [{ @@ -357,6 +583,7 @@ Variadic:$presentOperands, Variadic:$deviceptrOperands, Variadic:$attachOperands, + Variadic:$dataClauseOperands, OptionalAttr:$defaultAttr); let regions = (region AnyRegion:$region); @@ -372,6 +599,7 @@ let assemblyFormat = [{ oilist( `if` `(` $ifCond `)` + | `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)` | `copy` `(` $copyOperands `:` type($copyOperands) `)` | `copyin` `(` $copyinOperands `:` type($copyinOperands) `)` | `copyin_readonly` `(` $copyinReadonlyOperands `:` @@ -431,7 +659,8 @@ Variadic:$copyinOperands, Variadic:$createOperands, Variadic:$createZeroOperands, - Variadic:$attachOperands); + Variadic:$attachOperands, + Variadic:$dataClauseOperands); let extraClassDeclaration = [{ /// The number of data operands. @@ -452,6 +681,7 @@ | `create_zero` `(` $createZeroOperands `:` type($createZeroOperands) `)` | `attach` `(` $attachOperands `:` type($attachOperands) `)` + | `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)` ) attr-dict-with-keyword }]; @@ -486,6 +716,7 @@ Variadic:$copyoutOperands, Variadic:$deleteOperands, Variadic:$detachOperands, + Variadic:$dataClauseOperands, UnitAttr:$finalize); let extraClassDeclaration = [{ @@ -505,6 +736,7 @@ | `copyout` `(` $copyoutOperands `:` type($copyoutOperands) `)` | `delete` `(` $deleteOperands `:` type($deleteOperands) `)` | `detach` `(` $detachOperands `:` type($detachOperands) `)` + | `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)` ) attr-dict-with-keyword }]; @@ -517,7 +749,7 @@ // 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 --- /dev/null +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOpsTypes.td @@ -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 : TypeDef { + 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 --- /dev/null +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td @@ -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 diff --git a/mlir/lib/Dialect/OpenACC/CMakeLists.txt b/mlir/lib/Dialect/OpenACC/CMakeLists.txt --- a/mlir/lib/Dialect/OpenACC/CMakeLists.txt +++ b/mlir/lib/Dialect/OpenACC/CMakeLists.txt @@ -6,6 +6,9 @@ DEPENDS MLIROpenACCOpsIncGen + MLIROpenACCEnumsIncGen + MLIROpenACCAttributesIncGen + MLIROpenACCTypeInterfacesIncGen LINK_LIBS PUBLIC MLIRIR 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 @@ -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 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 @@ #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(*getContext()); + MemRefType::attachInterface(*getContext()); +} + +//===----------------------------------------------------------------------===// +// DataBounds +//===----------------------------------------------------------------------===// +LogicalResult acc::DataBounds::verify() { + auto extent = getExtent(); + auto upperbound = getUpperbound(); + if (!extent && !upperbound) { + return emitError("expected extent or upperbound."); + } + return success(); +} + +//===----------------------------------------------------------------------===// +// DevicePtrClause +//===----------------------------------------------------------------------===// +LogicalResult acc::DevicePtrClause::verify() { + if (getDataClause() != acc::DataClause::acc_deviceptr) { + return emitError("data clause associated with deviceptr operation must " + "match its intent"); + } + return success(); +} + +//===----------------------------------------------------------------------===// +// PresentClause +//===----------------------------------------------------------------------===// +LogicalResult acc::PresentClause::verify() { + if (getDataClause() != acc::DataClause::acc_present) { + return emitError( + "data clause associated with present operation must match its intent"); + } + return success(); +} + +//===----------------------------------------------------------------------===// +// CopyinClause +//===----------------------------------------------------------------------===// +LogicalResult acc::CopyinClause::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::CopyinClause::isCopyinReadonly() { + return getDataClause() == acc::DataClause::acc_copyin_readonly; +} + +//===----------------------------------------------------------------------===// +// CreateClause +//===----------------------------------------------------------------------===// +LogicalResult acc::CreateClause::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::CreateClause::isCreateZero() { + return getDataClause() == acc::DataClause::acc_create_zero; +} + +//===----------------------------------------------------------------------===// +// NoCreateClause +//===----------------------------------------------------------------------===// +LogicalResult acc::NoCreateClause::verify() { + if (getDataClause() != acc::DataClause::acc_no_create) { + return emitError("data clause associated with no_create operation must " + "match its intent"); + } + return success(); +} + +//===----------------------------------------------------------------------===// +// AttachClause +//===----------------------------------------------------------------------===// +LogicalResult acc::AttachClause::verify() { + if (getDataClause() != acc::DataClause::acc_attach) { + return emitError( + "data clause associated with attach operation must match its intent"); + } + return success(); +} + +//===----------------------------------------------------------------------===// +// GetDevicePtr +//===----------------------------------------------------------------------===// +LogicalResult acc::GetDevicePtr::verify() { + if (getDataClause() != acc::DataClause::acc_getdeviceptr) { + return emitError("getDevicePtr mismatch"); + } + return success(); +} + +//===----------------------------------------------------------------------===// +// CopyoutClause +//===----------------------------------------------------------------------===// +LogicalResult acc::CopyoutClause::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::CopyoutClause::isCopyoutZero() { + return getDataClause() == acc::DataClause::acc_copyout_zero; +} + +//===----------------------------------------------------------------------===// +// DeleteClause +//===----------------------------------------------------------------------===// +LogicalResult acc::DeleteClause::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(); +} + +//===----------------------------------------------------------------------===// +// DetachClause +//===----------------------------------------------------------------------===// +LogicalResult acc::DetachClause::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 @@ -45,7 +204,8 @@ regions.push_back(state.addRegion()); for (Region *region : regions) { - if (parser.parseRegion(*region, /*arguments=*/{}, /*argTypes=*/{})) + if (parser.parseRegion(*region, /*arguments=*/{}, + /*enableNameShadowing=*/{})) return failure(); } @@ -174,7 +334,8 @@ 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) { @@ -198,7 +359,8 @@ 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) { @@ -218,7 +380,7 @@ getCopyoutZeroOperands().size() + getCreateOperands().size() + getCreateZeroOperands().size() + getNoCreateOperands().size() + getPresentOperands().size() + getDevicePtrOperands().size() + - getAttachOperands().size(); + getAttachOperands().size() + getDataClauseOperands().size(); } Value KernelsOp::getDataOperand(unsigned i) { @@ -435,7 +597,7 @@ getCopyoutZeroOperands().size() + getCreateOperands().size() + getCreateZeroOperands().size() + getNoCreateOperands().size() + getPresentOperands().size() + getDeviceptrOperands().size() + - getAttachOperands().size(); + getAttachOperands().size() + getDataClauseOperands().size(); } Value DataOp::getDataOperand(unsigned i) { @@ -452,7 +614,7 @@ // 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"); @@ -475,7 +637,7 @@ unsigned ExitDataOp::getNumDataOperands() { return getCopyoutOperands().size() + getDeleteOperands().size() + - getDetachOperands().size(); + getDetachOperands().size() + getDataClauseOperands().size(); } Value ExitDataOp::getDataOperand(unsigned i) { @@ -499,7 +661,8 @@ // 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"); @@ -522,7 +685,8 @@ unsigned EnterDataOp::getNumDataOperands() { return getCopyinOperands().size() + getCreateOperands().size() + - getCreateZeroOperands().size() + getAttachOperands().size(); + getCreateZeroOperands().size() + getAttachOperands().size() + + getDataClauseOperands().size(); } Value EnterDataOp::getDataOperand(unsigned i) { @@ -625,3 +789,6 @@ #define GET_ATTRDEF_CLASSES #include "mlir/Dialect/OpenACC/OpenACCOpsAttributes.cpp.inc" + +#define GET_TYPEDEF_CLASSES +#include "mlir/Dialect/OpenACC/OpenACCOpsTypes.cpp.inc" 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 @@ -974,3 +974,145 @@ // 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>, %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> + acc.parallel dataOperands(%attach : memref>) { + } + acc.detach accPtr(%attach : memref>) {decomposedFrom = 10} + + %copyinparent = acc.copyin varPtr(%a : memref<10xf32>) varPtrPtr(%b : memref>) -> 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>, [[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> +// CHECK-NEXT: acc.parallel dataOperands([[ATTACH]] : memref>) { +// CHECK-NEXT: } +// CHECK-NEXT: acc.detach accPtr([[ATTACH]] : memref>) {decomposedFrom = 10 : i64} +// CHECK: [[COPYINP:%.*]] = acc.copyin varPtr([[ARGA]] : memref<10xf32>) varPtrPtr([[ARGB]] : memref>) -> 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}