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 @@ -95,6 +95,8 @@ def OpenACC_UpdateDevice : I64EnumAttrCase<"acc_update_device", 19>; def OpenACC_UseDevice : I64EnumAttrCase<"acc_use_device", 20>; def OpenACC_Reduction : I64EnumAttrCase<"acc_reduction", 21>; +def OpenACC_DeclareDeviceResident : I64EnumAttrCase<"acc_declare_device_resident", 22>; +def OpenACC_DeclareLink : I64EnumAttrCase<"acc_declare_link", 23>; def OpenACC_DataClauseEnum : I64EnumAttr<"DataClause", "data clauses supported by OpenACC", @@ -105,7 +107,7 @@ OpenACC_PrivateClause, OpenACC_FirstPrivateClause, OpenACC_IsDevicePtrClause, OpenACC_GetDevicePtrClause, OpenACC_UpdateHost, OpenACC_UpdateSelf, OpenACC_UpdateDevice, OpenACC_UseDevice, - OpenACC_Reduction, + OpenACC_Reduction, OpenACC_DeclareDeviceResident, OpenACC_DeclareLink, ]> { let cppNamespace = "::mlir::acc"; } @@ -181,7 +183,8 @@ // // The bounds are represented in rank order. Rank 0 (inner-most dimension) is // the first. -class OpenACC_DataEntryOp traits = []> : +class OpenACC_DataEntryOp traits = []> : OpenACC_Op { let arguments = (ins OpenACC_PointerLikeTypeInterface:$varPtr, @@ -193,7 +196,8 @@ OptionalAttr:$name); let results = (outs OpenACC_PointerLikeTypeInterface:$accPtr); - let description = [{ + let description = !strconcat(extraDescription, [{ + Description of arguments: - `varPtr`: The address of variable to copy. - `varPtrPtr`: Specifies the address of varPtr - only used when the variable copied is a field in a struct. This is important for OpenACC due to implicit @@ -211,7 +215,7 @@ - `implicit`: Whether this is an implicitly generated operation, such as copies done to satisfy "Variables with Implicitly Determined Data Attributes" in 2.6.2. - `name`: Holds the name of variable as specified in user clause (including bounds). - }]; + }]); let assemblyFormat = [{ `varPtr` `(` $varPtr `:` type($varPtr) `)` @@ -228,7 +232,7 @@ // 2.5.13 private clause //===----------------------------------------------------------------------===// def OpenACC_PrivateOp : OpenACC_DataEntryOp<"private", - "mlir::acc::DataClause::acc_private"> { + "mlir::acc::DataClause::acc_private", ""> { let summary = "Represents private semantics for acc private clause."; } @@ -236,7 +240,7 @@ // 2.5.14 firstprivate clause //===----------------------------------------------------------------------===// def OpenACC_FirstprivateOp : OpenACC_DataEntryOp<"firstprivate", - "mlir::acc::DataClause::acc_firstprivate"> { + "mlir::acc::DataClause::acc_firstprivate", ""> { let summary = "Represents firstprivate semantic for the acc firstprivate " "clause."; } @@ -245,7 +249,7 @@ // 2.5.15 reduction clause //===----------------------------------------------------------------------===// def OpenACC_ReductionOp : OpenACC_DataEntryOp<"reduction", - "mlir::acc::DataClause::acc_reduction"> { + "mlir::acc::DataClause::acc_reduction", ""> { let summary = "Represents reduction semantics for acc reduction clause."; } @@ -253,7 +257,7 @@ // 2.7.4 deviceptr clause //===----------------------------------------------------------------------===// def OpenACC_DevicePtrOp : OpenACC_DataEntryOp<"deviceptr", - "mlir::acc::DataClause::acc_deviceptr"> { + "mlir::acc::DataClause::acc_deviceptr", ""> { let summary = "Specifies that the variable pointer is a device pointer."; } @@ -261,7 +265,7 @@ // 2.7.5 present clause //===----------------------------------------------------------------------===// def OpenACC_PresentOp : OpenACC_DataEntryOp<"present", - "mlir::acc::DataClause::acc_present"> { + "mlir::acc::DataClause::acc_present", ""> { let summary = "Specifies that the variable is already present on device."; } @@ -269,7 +273,7 @@ // 2.7.7 copyin clause //===----------------------------------------------------------------------===// def OpenACC_CopyinOp : OpenACC_DataEntryOp<"copyin", - "mlir::acc::DataClause::acc_copyin"> { + "mlir::acc::DataClause::acc_copyin", ""> { let summary = "Represents copyin semantics for acc data clauses like acc " "copyin and acc copy."; @@ -283,7 +287,7 @@ // 2.7.9 create clause //===----------------------------------------------------------------------===// def OpenACC_CreateOp : OpenACC_DataEntryOp<"create", - "mlir::acc::DataClause::acc_create"> { + "mlir::acc::DataClause::acc_create", ""> { let summary = "Represents create semantics for acc data clauses like acc " "create and acc copyout."; @@ -297,7 +301,7 @@ // 2.7.10 no_create clause //===----------------------------------------------------------------------===// def OpenACC_NoCreateOp : OpenACC_DataEntryOp<"nocreate", - "mlir::acc::DataClause::acc_no_create"> { + "mlir::acc::DataClause::acc_no_create", ""> { let summary = "Represents acc no_create semantics."; } @@ -305,7 +309,7 @@ // 2.7.12 attach clause //===----------------------------------------------------------------------===// def OpenACC_AttachOp : OpenACC_DataEntryOp<"attach", - "mlir::acc::DataClause::acc_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."; @@ -315,20 +319,27 @@ // 3.2.23 acc_deviceptr //===----------------------------------------------------------------------===// // This is needed to get device address without the additional semantics in -// acc present. +// acc present. Effectively, it can be used to get "accPtr" for any variable. // 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."; + "mlir::acc::DataClause::acc_getdeviceptr", [{ + This operation is used to get the `accPtr` for a variable. This is often + used in conjunction with data exit operations when the data entry + operation is not visible. This operation can have a `dataClause` argument + that is any of the valid `mlir::acc::DataClause` entries. + \ + }]> { + let summary = "Gets device address if variable exists on device."; + let hasVerifier = 0; } //===----------------------------------------------------------------------===// // 2.14.4 device clause //===----------------------------------------------------------------------===// def OpenACC_UpdateDeviceOp : OpenACC_DataEntryOp<"update_device", - "mlir::acc::DataClause::acc_update_device"> { + "mlir::acc::DataClause::acc_update_device", ""> { let summary = "Represents acc update device semantics."; } @@ -336,10 +347,26 @@ // 2.8 use_device clause //===----------------------------------------------------------------------===// def OpenACC_UseDeviceOp : OpenACC_DataEntryOp<"use_device", - "mlir::acc::DataClause::acc_use_device"> { + "mlir::acc::DataClause::acc_use_device", ""> { let summary = "Represents acc use_device semantics."; } +//===----------------------------------------------------------------------===// +// 2.13.1 device_resident clause +//===----------------------------------------------------------------------===// +def OpenACC_DeclareDeviceResidentOp : OpenACC_DataEntryOp<"declare_device_resident", + "mlir::acc::DataClause::acc_declare_device_resident", ""> { + let summary = "Represents acc declare device_resident semantics."; +} + +//===----------------------------------------------------------------------===// +// 2.13.3 link clause +//===----------------------------------------------------------------------===// +def OpenACC_DeclareLinkOp : OpenACC_DataEntryOp<"declare_link", + "mlir::acc::DataClause::acc_declare_link", ""> { + let summary = "Represents acc declare link semantics."; +} + // 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 @@ -1140,6 +1167,136 @@ let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } +//===----------------------------------------------------------------------===// +// 2.13 Declare Directive +//===----------------------------------------------------------------------===// + +def OpenACC_DeclareEnterOp : OpenACC_Op<"declare_enter", []> { + let summary = "declare directive - entry to implicit data region"; + + let description = [{ + The "acc.declare_enter" operation represents the OpenACC declare directive + and captures the entry semantics to the implicit data region. + This operation is modeled similarly to "acc.enter_data". + + Example showing `acc declare create(a)`: + + ```mlir + %0 = acc.create varPtr(%a : !llvm.ptr) -> !llvm.ptr + acc.declare_enter dataOperands(%0 : !llvm.ptr) + ``` + }]; + + let arguments = (ins Variadic:$dataClauseOperands); + + let assemblyFormat = [{ + oilist( + `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)` + ) + attr-dict-with-keyword + }]; + + let hasVerifier = 1; +} + +def OpenACC_DeclareExitOp : OpenACC_Op<"declare_exit", []> { + let summary = "declare directive - exit from implicit data region"; + + let description = [{ + The "acc.declare_exit" operation represents the OpenACC declare directive + and captures the exit semantics from the implicit data region. + This operation is modeled similarly to "acc.exit_data". + + Example showing `acc declare device_resident(a)`: + + ```mlir + %0 = acc.getdeviceptr varPtr(%a : !llvm.ptr) -> !llvm.ptr {dataClause = 22} + acc.declare_exit dataOperands(%0 : !llvm.ptr) + acc.delete accPtr(%0 : !llvm.ptr) {dataClause = 22} + ``` + }]; + + let arguments = (ins Variadic:$dataClauseOperands); + + let assemblyFormat = [{ + oilist( + `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)` + ) + attr-dict-with-keyword + }]; + + let hasVerifier = 1; +} + +def OpenACC_GlobalConstructorOp : OpenACC_Op<"global_ctor", + [IsolatedFromAbove, Symbol]> { + let summary = "Used to hold construction operations associated with globals such as declare"; + + let description = [{ + The "acc.global_ctor" operation is used to capture OpenACC actions to apply + on globals (such as `acc declare`) at the entry to the implicit data region. + This operation is isolated and intended to be used in a module. + + Example showing `declare create` of global: + + ```mlir + llvm.mlir.global external @globalvar() : i32 { + %0 = llvm.mlir.constant(0 : i32) : i32 + llvm.return %0 : i32 + } + acc.global_ctor @acc_constructor { + %0 = llvm.mlir.addressof @globalvar : !llvm.ptr + %1 = acc.create varPtr(%0 : !llvm.ptr) -> !llvm.ptr + acc.declare_enter dataOperands(%1 : !llvm.ptr) + } + ``` + }]; + + let arguments = (ins SymbolNameAttr:$sym_name); + let regions = (region AnyRegion:$region); + + let assemblyFormat = [{ + $sym_name $region attr-dict-with-keyword + }]; + + let hasVerifier = 0; +} + +def OpenACC_GlobalDestructorOp : OpenACC_Op<"global_dtor", + [IsolatedFromAbove, Symbol]> { + let summary = "Used to hold destruction operations associated with globals such as declare"; + + let description = [{ + The "acc.global_dtor" operation is used to capture OpenACC actions to apply + on globals (such as `acc declare`) at the exit from the implicit data + region. This operation is isolated and intended to be used in a module. + + Example showing delete associated with `declare create` of global: + + ```mlir + llvm.mlir.global external @globalvar() : i32 { + %0 = llvm.mlir.constant(0 : i32) : i32 + llvm.return %0 : i32 + } + acc.global_dtor @acc_destructor { + %0 = llvm.mlir.addressof @globalvar : !llvm.ptr + %1 = acc.getdeviceptr varPtr(%0 : !llvm.ptr) -> !llvm.ptr {dataClause = 7} + acc.declare_exit dataOperands(%1 : !llvm.ptr) + acc.delete accPtr(%1 : !llvm.ptr) {dataClause = 7} + } + ``` + }]; + + let arguments = (ins SymbolNameAttr:$sym_name); + let regions = (region AnyRegion:$region); + + let assemblyFormat = [{ + $sym_name $region attr-dict-with-keyword + }]; + + let hasVerifier = 0; +} + //===----------------------------------------------------------------------===// // 2.14.1. Init Directive //===----------------------------------------------------------------------===// 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 @@ -171,19 +171,24 @@ } //===----------------------------------------------------------------------===// -// GetDevicePtrOp +// DeclareDeviceResidentOp //===----------------------------------------------------------------------===// -LogicalResult acc::GetDevicePtrOp::verify() { - // This operation is also created for use in unstructured constructs - // when we need an "accPtr" to feed to exit operation. Thus we test - // for those cases as well: - if (getDataClause() != acc::DataClause::acc_getdeviceptr && - getDataClause() != acc::DataClause::acc_copyout && - getDataClause() != acc::DataClause::acc_delete && - getDataClause() != acc::DataClause::acc_detach && - getDataClause() != acc::DataClause::acc_update_host && - getDataClause() != acc::DataClause::acc_update_self) - return emitError("getDevicePtr mismatch"); + +LogicalResult acc::DeclareDeviceResidentOp::verify() { + if (getDataClause() != acc::DataClause::acc_declare_device_resident) + return emitError("data clause associated with device_resident operation " + "must match its intent"); + return success(); +} + +//===----------------------------------------------------------------------===// +// DeclareLinkOp +//===----------------------------------------------------------------------===// + +LogicalResult acc::DeclareLinkOp::verify() { + if (getDataClause() != acc::DataClause::acc_declare_link) + return emitError( + "data clause associated with link operation must match its intent"); return success(); } @@ -214,7 +219,12 @@ // Test for all clauses this operation can be decomposed from: if (getDataClause() != acc::DataClause::acc_delete && getDataClause() != acc::DataClause::acc_create && - getDataClause() != acc::DataClause::acc_create_zero) + getDataClause() != acc::DataClause::acc_create_zero && + getDataClause() != acc::DataClause::acc_copyin && + getDataClause() != acc::DataClause::acc_copyin_readonly && + getDataClause() != acc::DataClause::acc_present && + getDataClause() != acc::DataClause::acc_declare_device_resident && + getDataClause() != acc::DataClause::acc_declare_link) return emitError( "data clause associated with delete operation must match its intent" " or specify original clause this operation was decomposed from"); @@ -965,6 +975,41 @@ results.add>(context); } +//===----------------------------------------------------------------------===// +// DeclareEnterOp +//===----------------------------------------------------------------------===// + +template +static LogicalResult checkDeclareOperands(Op &op, + const mlir::ValueRange &operands) { + if (operands.empty()) + return emitError( + op->getLoc(), + "at least one operand must appear on the declare operation"); + + for (mlir::Value operand : operands) + if (!mlir::isa( + operand.getDefiningOp())) + return op.emitError( + "expect valid declare data entry operation or acc.getdeviceptr " + "as defining op"); + return success(); +} + +LogicalResult acc::DeclareEnterOp::verify() { + return checkDeclareOperands(*this, this->getDataClauseOperands()); +} + +//===----------------------------------------------------------------------===// +// DeclareExitOp +//===----------------------------------------------------------------------===// + +LogicalResult acc::DeclareExitOp::verify() { + return checkDeclareOperands(*this, this->getDataClauseOperands()); +} + //===----------------------------------------------------------------------===// // InitOp //===----------------------------------------------------------------------===// 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 @@ -1538,3 +1538,99 @@ // CHECK-LABEL: func.func @acc_reduc_test( // CHECK-SAME: %[[ARG0:.*]]: i64) // CHECK: acc.serial reduction(@reduction_add_i64 -> %[[ARG0]] : i64) + +// ----- + +func.func @testdeclareop(%a: memref, %b: memref, %c: memref) -> () { + %0 = acc.copyin varPtr(%a : memref) -> memref + // copyin(zero) + %1 = acc.copyin varPtr(%b : memref) -> memref {dataClause = 2} + // copy + %2 = acc.copyin varPtr(%c : memref) -> memref {dataClause = 3} + acc.declare_enter dataOperands(%0, %1, %2 : memref, memref, memref) + + %3 = acc.create varPtr(%a : memref) -> memref + // copyout + %4 = acc.create varPtr(%b : memref) -> memref {dataClause = 4} + %5 = acc.present varPtr(%c : memref) -> memref + acc.declare_enter dataOperands(%3, %4, %5 : memref, memref, memref) + + %6 = acc.deviceptr varPtr(%a : memref) -> memref + %7 = acc.declare_device_resident varPtr(%b : memref) -> memref + %8 = acc.declare_link varPtr(%c : memref) -> memref + acc.declare_enter dataOperands(%6, %7, %8 : memref, memref, memref) + + acc.declare_exit dataOperands(%7, %8 : memref, memref) + acc.delete accPtr(%7 : memref) {dataClause = 22 } + acc.delete accPtr(%8 : memref) {dataClause = 23 } + + acc.declare_exit dataOperands(%3, %4, %5 : memref, memref, memref) + acc.delete accPtr(%3 : memref) {dataClause = 7 } + acc.copyout accPtr(%4 : memref) to varPtr(%b : memref) + acc.delete accPtr(%5 : memref) {dataClause = 6 } + + acc.declare_exit dataOperands(%0, %1, %2 : memref, memref, memref) + acc.delete accPtr(%0 : memref) {dataClause = 1 } + acc.delete accPtr(%1 : memref) {dataClause = 2 } + acc.copyout accPtr(%2 : memref) to varPtr(%c : memref) { dataClause = 3 } + + return +} + +// CHECK-LABEL: func.func @testdeclareop( +// CHECK-SAME: %[[ARGA:.*]]: memref, %[[ARGB:.*]]: memref, %[[ARGC:.*]]: memref) +// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr(%[[ARGA]] : memref) -> memref +// CHECK-NEXT: %[[COPYINRO:.*]] = acc.copyin varPtr(%[[ARGB]] : memref) -> memref {dataClause = 2 : i64} +// CHECK-NEXT: %[[COPY:.*]] = acc.copyin varPtr(%[[ARGC]] : memref) -> memref {dataClause = 3 : i64} +// CHECK-NEXT: acc.declare_enter dataOperands(%[[COPYIN]], %[[COPYINRO]], %[[COPY]] : memref, memref, memref) +// CHECK: %[[CREATE:.*]] = acc.create varPtr(%[[ARGA]] : memref) -> memref +// CHECK-NEXT: %[[COPYOUT:.*]] = acc.create varPtr(%[[ARGB]] : memref) -> memref {dataClause = 4 : i64} +// CHECK-NEXT: %[[PRESENT:.*]] = acc.present varPtr(%[[ARGC]] : memref) -> memref +// CHECK-NEXT: acc.declare_enter dataOperands(%[[CREATE]], %[[COPYOUT]], %[[PRESENT]] : memref, memref, memref) +// CHECK: %[[DEVICEPTR:.*]] = acc.deviceptr varPtr(%[[ARGA]] : memref) -> memref +// CHECK-NEXT: %[[DEVICERES:.*]] = acc.declare_device_resident varPtr(%[[ARGB]] : memref) -> memref +// CHECK-NEXT: %[[LINK:.*]] = acc.declare_link varPtr(%[[ARGC]] : memref) -> memref +// CHECK-NEXT: acc.declare_enter dataOperands(%[[DEVICEPTR]], %[[DEVICERES]], %[[LINK]] : memref, memref, memref) +// CHECK: acc.declare_exit dataOperands(%[[DEVICERES]], %[[LINK]] : memref, memref) +// CHECK-NEXT: acc.delete accPtr(%[[DEVICERES]] : memref) {dataClause = 22 : i64} +// CHECK-NEXT: acc.delete accPtr(%[[LINK]] : memref) {dataClause = 23 : i64} +// CHECK: acc.declare_exit dataOperands(%[[CREATE]], %[[COPYOUT]], %[[PRESENT]] : memref, memref, memref) +// CHECK-NEXT: acc.delete accPtr(%[[CREATE]] : memref) {dataClause = 7 : i64} +// CHECK-NEXT: acc.copyout accPtr(%[[COPYOUT]] : memref) to varPtr(%[[ARGB]] : memref) +// CHECK-NEXT: acc.delete accPtr(%[[PRESENT]] : memref) {dataClause = 6 : i64} +// CHECK: acc.declare_exit dataOperands(%[[COPYIN]], %[[COPYINRO]], %[[COPY]] : memref, memref, memref) +// CHECK-NEXT: acc.delete accPtr(%[[COPYIN]] : memref) {dataClause = 1 : i64} +// CHECK-NEXT: acc.delete accPtr(%[[COPYINRO]] : memref) {dataClause = 2 : i64} +// CHECK-NEXT: acc.copyout accPtr(%[[COPY]] : memref) to varPtr(%[[ARGC]] : memref) {dataClause = 3 : i64} + +// ----- + +llvm.mlir.global external @globalvar() : i32 { + %0 = llvm.mlir.constant(0 : i32) : i32 + llvm.return %0 : i32 +} + +acc.global_ctor @acc_constructor { + %0 = llvm.mlir.addressof @globalvar : !llvm.ptr + %1 = acc.create varPtr(%0 : !llvm.ptr) -> !llvm.ptr + acc.declare_enter dataOperands(%1 : !llvm.ptr) + acc.terminator +} + +acc.global_dtor @acc_destructor { + %0 = llvm.mlir.addressof @globalvar : !llvm.ptr + %1 = acc.getdeviceptr varPtr(%0 : !llvm.ptr) -> !llvm.ptr { dataClause = 7} + acc.declare_exit dataOperands(%1 : !llvm.ptr) + acc.delete accPtr(%1 : !llvm.ptr) + acc.terminator +} + +// CHECK-LABEL: acc.global_ctor @acc_constructor +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @globalvar : !llvm.ptr +// CHECK-NEXT: %[[CREATE:.*]] = acc.create varPtr(%[[ADDR]] : !llvm.ptr) -> !llvm.ptr +// CHECK-NEXT: acc.declare_enter dataOperands(%[[CREATE]] : !llvm.ptr) +// CHECK: acc.global_dtor @acc_destructor +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @globalvar : !llvm.ptr +// CHECK-NEXT: %[[DELETE:.*]] = acc.getdeviceptr varPtr(%[[ADDR]] : !llvm.ptr) -> !llvm.ptr {dataClause = 7 : i64} +// CHECK-NEXT: acc.declare_exit dataOperands(%[[DELETE]] : !llvm.ptr) +// CHECK-NEXT: acc.delete accPtr(%[[DELETE]] : !llvm.ptr)