[openacc] Add attribute to hold declare data clause information

For variables in declare clauses, their producing operation should be
marked with the data clause for ease of lookup and consistency
verification. Thus add an attribute that can be used for this purpose
plus verification that declare data operation matches the declare
data clause on variable.

Reviewed By: clementval

Differential Revision: https://reviews.llvm.org/D155640
This commit is contained in:
Razvan Lupusoru
2023-07-18 13:39:51 -07:00
parent f9d1895d65
commit 132c376a33
4 changed files with 104 additions and 6 deletions

View File

@@ -34,6 +34,22 @@
#define GET_OP_CLASSES
#include "mlir/Dialect/OpenACC/OpenACCOps.h.inc"
#define ACC_DATA_ENTRY_OPS \
mlir::acc::CopyinOp, mlir::acc::CreateOp, mlir::acc::PresentOp, \
mlir::acc::NoCreateOp, mlir::acc::AttachOp, mlir::acc::DevicePtrOp, \
mlir::acc::GetDevicePtrOp, mlir::acc::PrivateOp, \
mlir::acc::FirstprivateOp, mlir::acc::UpdateDeviceOp, \
mlir::acc::UseDeviceOp, mlir::acc::ReductionOp, \
mlir::acc::DeclareDeviceResidentOp, mlir::acc::DeclareLinkOp
#define ACC_COMPUTE_CONSTRUCT_OPS \
mlir::acc::ParallelOp, mlir::acc::KernelsOp, mlir::acc::SerialOp
#define ACC_DATA_CONSTRUCT_OPS \
mlir::acc::DataOp, mlir::acc::EnterDataOp, mlir::acc::ExitDataOp, \
mlir::acc::UpdateOp, mlir::acc::HostDataOp, \
mlir::acc::DeclareEnterOp mlir::acc::DeclareExitOp
#define ACC_COMPUTE_AND_DATA_CONSTRUCT_OPS \
ACC_COMPUTE_CONSTRUCT_OPS, ACC_DATA_CONSTRUCT_OPS
namespace mlir {
namespace acc {
@@ -48,6 +64,20 @@ namespace acc {
/// combined and the final mapping value would be 5 (4 | 1).
enum OpenACCExecMapping { NONE = 0, VECTOR = 1, WORKER = 2, GANG = 4 };
/// Used to obtain the `varPtr` from a data entry operation.
/// Returns empty value if not a data entry operation.
mlir::Value getVarPtr(mlir::Operation *accDataEntryOp);
/// Used to obtain the `dataClause` from a data entry operation.
/// Returns empty optional if not a data entry operation.
std::optional<mlir::acc::DataClause>
getDataClause(mlir::Operation *accDataEntryOp);
/// Used to obtain the attribute name for declare.
static constexpr StringLiteral getDeclareAttrName() {
return StringLiteral("acc.declare");
}
} // namespace acc
} // namespace mlir

View File

@@ -116,6 +116,25 @@ def OpenACC_DataClauseEnum : I64EnumAttr<"DataClause",
def OpenACC_DataClauseAttr : EnumAttr<OpenACC_Dialect, OpenACC_DataClauseEnum,
"data_clause">;
class OpenACC_Attr<string name, string attrMnemonic,
list<Trait> traits = [],
string baseCppClass = "::mlir::Attribute">
: AttrDef<OpenACC_Dialect, name, traits, baseCppClass> {
let mnemonic = attrMnemonic;
}
// Attribute to describe the declare data clause used on variable.
// Intended to be used at the variable creation site (on the global op or the
// corresponding allocation operation). This is used in conjunction with the
// declare operations (`acc.declare_enter` and `acc.declare_exit`) since those
// describe how the data action is performed. The attribute itself makes it
// easier to find out whether the variable is in a declare clause and what kind
// of clause it is.
def DeclareAttr : OpenACC_Attr<"Declare", "declare"> {
let parameters = (ins "DataClauseAttr":$dataClause);
let assemblyFormat = "`<` struct(params) `>`";
}
// 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",

View File

@@ -987,7 +987,7 @@ static LogicalResult checkDeclareOperands(Op &op,
op->getLoc(),
"at least one operand must appear on the declare operation");
for (mlir::Value operand : operands)
for (mlir::Value operand : operands) {
if (!mlir::isa<acc::CopyinOp, acc::CopyoutOp, acc::CreateOp,
acc::DevicePtrOp, acc::GetDevicePtrOp, acc::PresentOp,
acc::DeclareDeviceResidentOp, acc::DeclareLinkOp>(
@@ -995,6 +995,32 @@ static LogicalResult checkDeclareOperands(Op &op,
return op.emitError(
"expect valid declare data entry operation or acc.getdeviceptr "
"as defining op");
mlir::Value varPtr{getVarPtr(operand.getDefiningOp())};
assert(varPtr && "declare operands can only be data entry operations which "
"must have varPtr");
std::optional<mlir::acc::DataClause> dataClauseOptional{
getDataClause(operand.getDefiningOp())};
assert(dataClauseOptional.has_value() &&
"declare operands can only be data entry operations which must have "
"dataClause");
// If varPtr has no defining op - there is nothing to check further.
if (!varPtr.getDefiningOp())
continue;
// Check that the varPtr has a declare attribute.
auto declareAttribute{
varPtr.getDefiningOp()->getAttr(mlir::acc::getDeclareAttrName())};
if (!declareAttribute)
return op.emitError(
"expect declare attribute on variable in declare operation");
if (llvm::cast<DataClauseAttr>(declareAttribute).getValue() !=
dataClauseOptional.value())
return op.emitError(
"expect matching declare attribute on variable in declare operation");
}
return success();
}
@@ -1106,3 +1132,26 @@ LogicalResult acc::WaitOp::verify() {
#define GET_TYPEDEF_CLASSES
#include "mlir/Dialect/OpenACC/OpenACCOpsTypes.cpp.inc"
//===----------------------------------------------------------------------===//
// acc dialect utilities
//===----------------------------------------------------------------------===//
mlir::Value mlir::acc::getVarPtr(mlir::Operation *accDataEntryOp) {
auto varPtr{llvm::TypeSwitch<mlir::Operation *, mlir::Value>(accDataEntryOp)
.Case<ACC_DATA_ENTRY_OPS>(
[&](auto entry) { return entry.getVarPtr(); })
.Default([&](mlir::Operation *) { return mlir::Value(); })};
return varPtr;
}
std::optional<mlir::acc::DataClause>
mlir::acc::getDataClause(mlir::Operation *accDataEntryOp) {
auto dataClause{
llvm::TypeSwitch<mlir::Operation *, std::optional<mlir::acc::DataClause>>(
accDataEntryOp)
.Case<ACC_DATA_ENTRY_OPS>(
[&](auto entry) { return entry.getDataClause(); })
.Default([&](mlir::Operation *) { return std::nullopt; })};
return dataClause;
}

View File

@@ -1605,20 +1605,20 @@ func.func @testdeclareop(%a: memref<f32>, %b: memref<f32>, %c: memref<f32>) -> (
// -----
llvm.mlir.global external @globalvar() : i32 {
llvm.mlir.global external @globalvar() { acc.declare = #acc<data_clause acc_create> } : i32 {
%0 = llvm.mlir.constant(0 : i32) : i32
llvm.return %0 : i32
}
acc.global_ctor @acc_constructor {
%0 = llvm.mlir.addressof @globalvar : !llvm.ptr<i32>
%0 = llvm.mlir.addressof @globalvar { acc.declare = #acc<data_clause acc_create> } : !llvm.ptr<i32>
%1 = acc.create varPtr(%0 : !llvm.ptr<i32>) -> !llvm.ptr<i32>
acc.declare_enter dataOperands(%1 : !llvm.ptr<i32>)
acc.terminator
}
acc.global_dtor @acc_destructor {
%0 = llvm.mlir.addressof @globalvar : !llvm.ptr<i32>
%0 = llvm.mlir.addressof @globalvar { acc.declare = #acc<data_clause acc_create> } : !llvm.ptr<i32>
%1 = acc.getdeviceptr varPtr(%0 : !llvm.ptr<i32>) -> !llvm.ptr<i32> { dataClause = #acc<data_clause acc_create>}
acc.declare_exit dataOperands(%1 : !llvm.ptr<i32>)
acc.delete accPtr(%1 : !llvm.ptr<i32>)
@@ -1626,11 +1626,11 @@ acc.global_dtor @acc_destructor {
}
// CHECK-LABEL: acc.global_ctor @acc_constructor
// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @globalvar : !llvm.ptr<i32>
// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @globalvar {acc.declare = #acc<data_clause acc_create>} : !llvm.ptr<i32>
// CHECK-NEXT: %[[CREATE:.*]] = acc.create varPtr(%[[ADDR]] : !llvm.ptr<i32>) -> !llvm.ptr<i32>
// CHECK-NEXT: acc.declare_enter dataOperands(%[[CREATE]] : !llvm.ptr<i32>)
// CHECK: acc.global_dtor @acc_destructor
// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @globalvar : !llvm.ptr<i32>
// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @globalvar {acc.declare = #acc<data_clause acc_create>} : !llvm.ptr<i32>
// CHECK-NEXT: %[[DELETE:.*]] = acc.getdeviceptr varPtr(%[[ADDR]] : !llvm.ptr<i32>) -> !llvm.ptr<i32> {dataClause = #acc<data_clause acc_create>}
// CHECK-NEXT: acc.declare_exit dataOperands(%[[DELETE]] : !llvm.ptr<i32>)
// CHECK-NEXT: acc.delete accPtr(%[[DELETE]] : !llvm.ptr<i32>)