3720 lines
160 KiB
TableGen
3720 lines
160 KiB
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.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Defines MLIR OpenACC operations.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef OPENACC_OPS
|
|
#define OPENACC_OPS
|
|
|
|
include "mlir/Interfaces/ControlFlowInterfaces.td"
|
|
include "mlir/Interfaces/LoopLikeInterface.td"
|
|
include "mlir/Interfaces/SideEffectInterfaces.td"
|
|
include "mlir/IR/BuiltinTypes.td"
|
|
include "mlir/IR/EnumAttr.td"
|
|
include "mlir/IR/OpBase.td"
|
|
include "mlir/IR/SymbolInterfaces.td"
|
|
include "mlir/Dialect/OpenACC/OpenACCAttributes.td"
|
|
include "mlir/Dialect/OpenACC/OpenACCBase.td"
|
|
include "mlir/Dialect/OpenACC/OpenACCOpsTypes.td"
|
|
include "mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td"
|
|
include "mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td"
|
|
include "mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td"
|
|
include "mlir/Dialect/OpenACCMPCommon/Interfaces/OpenACCMPOpsInterfaces.td"
|
|
|
|
// AccCommon requires definition of OpenACC_Dialect.
|
|
include "mlir/Dialect/OpenACC/AccCommon.td"
|
|
|
|
// Base class for OpenACC dialect ops.
|
|
class OpenACC_Op<string mnemonic, list<Trait> traits = []> :
|
|
Op<OpenACC_Dialect, mnemonic, traits>;
|
|
|
|
// Reduction operation enumeration.
|
|
def OpenACC_ReductionOperatorNone : I32EnumAttrCase<"AccNone", 0, "none">;
|
|
def OpenACC_ReductionOperatorAdd : I32EnumAttrCase<"AccAdd", 1, "add">;
|
|
def OpenACC_ReductionOperatorMul : I32EnumAttrCase<"AccMul", 2, "mul">;
|
|
// When NaNs or signed zeros are possible, AccMax and AccMin parallel reductions
|
|
// cannot guarantee stable results for floating-point values.
|
|
// As such, auto-parallelization of such reductions cannot be done.
|
|
// The FrontEnds can use alternative max/min reduction kinds (see below)
|
|
// to enable auto-parallelization.
|
|
def OpenACC_ReductionOperatorMax : I32EnumAttrCase<"AccMax", 3, "max">;
|
|
def OpenACC_ReductionOperatorMin : I32EnumAttrCase<"AccMin", 4, "min">;
|
|
def OpenACC_ReductionOperatorAnd : I32EnumAttrCase<"AccIand", 5, "iand">;
|
|
def OpenACC_ReductionOperatorOr : I32EnumAttrCase<"AccIor", 6, "ior">;
|
|
def OpenACC_ReductionOperatorXor : I32EnumAttrCase<"AccXor", 7, "xor">;
|
|
def OpenACC_ReductionOperatorLogEqv : I32EnumAttrCase<"AccEqv", 8, "eqv">;
|
|
def OpenACC_ReductionOperatorLogNeqv : I32EnumAttrCase<"AccNeqv", 9, "neqv">;
|
|
def OpenACC_ReductionOperatorLogAnd : I32EnumAttrCase<"AccLand", 10, "land">;
|
|
def OpenACC_ReductionOperatorLogOr : I32EnumAttrCase<"AccLor", 11, "lor">;
|
|
// The following reduction operators correspond to arith::AtomicRMWKind kinds
|
|
// named alike. They can only be applied to floating-point types.
|
|
// These reductions can be auto-parallelized.
|
|
def OpenACC_ReductionOperatorMaximum
|
|
: I32EnumAttrCase<"AccMaximumf", 12, "maximumf">;
|
|
def OpenACC_ReductionOperatorMinimum
|
|
: I32EnumAttrCase<"AccMinimumf", 13, "minimumf">;
|
|
def OpenACC_ReductionOperatorMaxnum
|
|
: I32EnumAttrCase<"AccMaxnumf", 14, "maxnumf">;
|
|
def OpenACC_ReductionOperatorMinnum
|
|
: I32EnumAttrCase<"AccMinnumf", 15, "minnumf">;
|
|
|
|
def OpenACC_ReductionOperator
|
|
: I32EnumAttr<
|
|
"ReductionOperator",
|
|
// Built-in reduction operations supported by OpenACC
|
|
// according OpenACC 3.3:
|
|
//
|
|
// |-------------------|----------------------|
|
|
// | Language operator | |
|
|
// |-------------------| ReductionOperator |
|
|
// | C/C++ | Fortran | |
|
|
// |------------------------------------------|
|
|
// | + | + | add |
|
|
// | * | * | mul |
|
|
// | max | max | max/maximumf/maxnumf |
|
|
// | min | min | min/minimumf/minnumf |
|
|
// | & | iand | iand |
|
|
// | | | ior | ior |
|
|
// | ^ | ieor | xor |
|
|
// | && | .and. | land |
|
|
// | || | .or. | lor |
|
|
// | | .eqv. | eqv |
|
|
// | | .neqv. | neqv |
|
|
// |------------------------------------------|
|
|
//
|
|
// The different max/min ReductionOperator's have different behavior
|
|
// when the arguments may be NaNs or signed zeros:
|
|
// * max/min - in general, produces inconsistent results
|
|
// in parallel execution, because it is not commutative.
|
|
// The max/min returns the second argument,
|
|
// when one of the arguments is NaN,
|
|
// or both arguments are zeros regardless of the sign.
|
|
// * maximumf/minimumf - safe to parallelize, corresponds to
|
|
// maximum/minimum defined in IEEE-754-2019.
|
|
// * maxnumf/minnumf - safe to parallelize, corresponds to
|
|
// maxNum/minNum defined in IEEE-754-2008.
|
|
"built-in reduction operations supported by OpenACC",
|
|
[OpenACC_ReductionOperatorNone, OpenACC_ReductionOperatorAdd,
|
|
OpenACC_ReductionOperatorMul, OpenACC_ReductionOperatorMax,
|
|
OpenACC_ReductionOperatorMin, OpenACC_ReductionOperatorAnd,
|
|
OpenACC_ReductionOperatorOr, OpenACC_ReductionOperatorXor,
|
|
OpenACC_ReductionOperatorLogEqv, OpenACC_ReductionOperatorLogNeqv,
|
|
OpenACC_ReductionOperatorLogAnd, OpenACC_ReductionOperatorLogOr,
|
|
OpenACC_ReductionOperatorMaximum, OpenACC_ReductionOperatorMinimum,
|
|
OpenACC_ReductionOperatorMaxnum, OpenACC_ReductionOperatorMinnum]> {
|
|
let genSpecializedAttr = 0;
|
|
let cppNamespace = "::mlir::acc";
|
|
}
|
|
def OpenACC_ReductionOperatorAttr : EnumAttr<OpenACC_Dialect,
|
|
OpenACC_ReductionOperator,
|
|
"reduction_operator"> {
|
|
let assemblyFormat = [{ ```<` $value `>` }];
|
|
}
|
|
|
|
// OpenACC variable type categorization. This is needed because OpenACC
|
|
// dialect is used with other dialects, and each dialect defines its own
|
|
// types. Thus, in order to be able to classify types and apply right semantics,
|
|
// it is needed to ensure the types can be categorized.
|
|
def OpenACC_VariableTypeUncategorized : I32BitEnumAttrCaseNone<"uncategorized">;
|
|
|
|
// The OpenACC spec definition of scalar type is as follows (from 3.3 spec,
|
|
// line 5454):
|
|
// Scalar datatype - an intrinsic or built-in datatype that is not an array or
|
|
// aggregate datatype. In Fortran, scalar datatypes are integer, real, double
|
|
// precision, complex, or logical. In C, scalar datatypes are char (signed or
|
|
// unsigned), int (signed or unsigned, with optional short, long or long long
|
|
// attribute), enum, float, double, long double, Complex (with optional float
|
|
// or long attribute), or any pointer datatype. In C++, scalar datatypes are
|
|
// char (signed or unsigned), wchar t, int (signed or unsigned, with optional
|
|
// short, long or long long attribute), enum, bool, float, double, long double,
|
|
// or any pointer datatype. Not all implementations or targets will support all
|
|
// of these datatypes.
|
|
// From an MLIR type perspective, the types that those language types map to
|
|
// will be categorized as scalar.
|
|
def OpenACC_VariableTypeScalar : I32BitEnumAttrCaseBit<"scalar", 0>;
|
|
|
|
// Not in OpenACC spec glossary as its own definition but used throughout the
|
|
// spec. One definition of array that can be assumed for purposes of type
|
|
// categorization is that it is a collection of elements of same type.
|
|
def OpenACC_VariableTypeArray : I32BitEnumAttrCaseBit<"array", 1>;
|
|
|
|
// The OpenACC spec definition of composite type is as follows (from 3.3 spec,
|
|
// line 5354):
|
|
// Composite datatype - a derived type in Fortran, or a struct or union type in
|
|
// C, or a class, struct, or union type in C++. (This is different from the use
|
|
// of the term composite data type in the C and C++ languages.)
|
|
def OpenACC_VariableTypeComposite : I32BitEnumAttrCaseBit<"composite", 2>;
|
|
|
|
// The OpenACC spec uses the type category "aggregate" to capture both arrays
|
|
// and composite types. However, it includes types which do not fall in either
|
|
// of those categories. Thus create a case for the others.
|
|
// For example, reading the definition of "Aggregate Variables" in the 3.3
|
|
// spec line 5346 shows this distinction:
|
|
// Aggregate variables - a variable of any non-scalar datatype, including array
|
|
// or composite variables. In Fortran, this includes any variable with
|
|
// allocatable or pointer attribute and character variables
|
|
def OpenACC_VariableTypeOtherNonScalar : I32BitEnumAttrCaseBit<"nonscalar", 3>;
|
|
|
|
// The OpenACC spec definition of aggregate type is as follows (from 3.3 spec,
|
|
// line 5342):
|
|
// Aggregate datatype - any non-scalar datatype such as array and composite
|
|
// datatypes. In Fortran, aggregate datatypes include arrays, derived types,
|
|
// character types. In C, aggregate datatypes include arrays, targets of
|
|
// pointers, structs, and unions. In C++, aggregate datatypes include arrays,
|
|
// targets of pointers, classes, structs, and unions.
|
|
def OpenACC_VariableTypeAggregate : I32BitEnumAttrCaseGroup<"aggregate",
|
|
[OpenACC_VariableTypeArray, OpenACC_VariableTypeComposite,
|
|
OpenACC_VariableTypeOtherNonScalar]>;
|
|
|
|
def OpenACC_VariableTypeCategory : I32BitEnumAttr<
|
|
"VariableTypeCategory",
|
|
"Captures different type categories described in OpenACC spec",
|
|
[
|
|
OpenACC_VariableTypeUncategorized, OpenACC_VariableTypeScalar,
|
|
OpenACC_VariableTypeArray, OpenACC_VariableTypeComposite,
|
|
OpenACC_VariableTypeOtherNonScalar, OpenACC_VariableTypeAggregate]> {
|
|
let separator = ",";
|
|
let cppNamespace = "::mlir::acc";
|
|
let genSpecializedAttr = 0;
|
|
let printBitEnumPrimaryGroups = 1;
|
|
}
|
|
|
|
// These are parallelism determination modes for `acc loop`.
|
|
// In the enum names, we use the "loop_" prefix because "auto" is
|
|
// a language keyword - and thus for consistency all other cases
|
|
// do the same.
|
|
def OpenACC_LoopSeq : I32EnumAttrCase<"loop_seq", 0>;
|
|
def OpenACC_LoopAuto : I32EnumAttrCase<"loop_auto", 1>;
|
|
def OpenACC_LoopIndependent : I32EnumAttrCase<"loop_independent", 2>;
|
|
|
|
def OpenACC_LoopParMode : I32EnumAttr<
|
|
"LoopParMode",
|
|
"Encodes the options for loop parallelism determination mode",
|
|
[
|
|
OpenACC_LoopAuto, OpenACC_LoopIndependent,
|
|
OpenACC_LoopSeq]> {
|
|
let cppNamespace = "::mlir::acc";
|
|
let genSpecializedAttr = 0;
|
|
}
|
|
|
|
// Parallelism level (gang/worker/vector/seq).
|
|
// GangDim1 is the default gang level (equivalent to just "gang").
|
|
// GangDim2/GangDim3 are for gang(dim:2) and gang(dim:3).
|
|
def OpenACC_ParLevelSeq : I32EnumAttrCase<"seq", 0>;
|
|
def OpenACC_ParLevelGangDim1 : I32EnumAttrCase<"gang_dim1", 1>;
|
|
def OpenACC_ParLevelGangDim2 : I32EnumAttrCase<"gang_dim2", 2>;
|
|
def OpenACC_ParLevelGangDim3 : I32EnumAttrCase<"gang_dim3", 3>;
|
|
def OpenACC_ParLevelWorker : I32EnumAttrCase<"worker", 4>;
|
|
def OpenACC_ParLevelVector : I32EnumAttrCase<"vector", 5>;
|
|
|
|
def OpenACC_ParLevel : I32EnumAttr<"ParLevel",
|
|
"Parallelism level (gang/worker/vector/seq)",
|
|
[OpenACC_ParLevelSeq,
|
|
OpenACC_ParLevelGangDim1, OpenACC_ParLevelGangDim2,
|
|
OpenACC_ParLevelGangDim3,
|
|
OpenACC_ParLevelWorker, OpenACC_ParLevelVector]> {
|
|
let genSpecializedAttr = 0;
|
|
let cppNamespace = "::mlir::acc";
|
|
}
|
|
|
|
def OpenACC_ParLevelAttr : EnumAttr<OpenACC_Dialect,
|
|
OpenACC_ParLevel,
|
|
"par_level"> {
|
|
let assemblyFormat = [{ ```<` $value `>` }];
|
|
}
|
|
|
|
def OpenACC_PrivateRecipe : I32EnumAttrCase<"private_recipe", 0>;
|
|
def OpenACC_FirstprivateRecipe : I32EnumAttrCase<"firstprivate_recipe", 1>;
|
|
def OpenACC_ReductionRecipe : I32EnumAttrCase<"reduction_recipe", 2>;
|
|
|
|
def OpenACC_RecipeKind : I32EnumAttr<
|
|
"RecipeKind",
|
|
"Encodes the options for kinds of recipes availabie in acc dialect",
|
|
[
|
|
OpenACC_PrivateRecipe, OpenACC_FirstprivateRecipe,
|
|
OpenACC_ReductionRecipe]> {
|
|
let cppNamespace = "::mlir::acc";
|
|
let genSpecializedAttr = 0;
|
|
}
|
|
|
|
def OpenACC_RecipeKindAttr : EnumAttr<OpenACC_Dialect,
|
|
OpenACC_RecipeKind,
|
|
"recipe_kind"> {
|
|
let assemblyFormat = [{ ```<` $value `>` }];
|
|
}
|
|
|
|
// 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">;
|
|
def OpenACC_MappableType : TypeAlias<OpenACC_MappableTypeInterface,
|
|
"mappable type">;
|
|
|
|
def OpenACC_AnyPointerOrMappableLike : TypeConstraint<Or<[OpenACC_PointerLikeType.predicate,
|
|
OpenACC_MappableType.predicate]>, "any pointer or mappable">;
|
|
def OpenACC_AnyPointerOrMappableType : Type<OpenACC_AnyPointerOrMappableLike.predicate,
|
|
"any pointer or mappable">;
|
|
|
|
// 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_UpdateHost : I64EnumAttrCase<"acc_update_host", 17>;
|
|
def OpenACC_UpdateSelf : I64EnumAttrCase<"acc_update_self", 18>;
|
|
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_Cache : I64EnumAttrCase<"acc_cache", 24>;
|
|
def OpenACC_CacheReadonly : I64EnumAttrCase<"acc_cache_readonly", 25>;
|
|
|
|
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, OpenACC_UpdateHost,
|
|
OpenACC_UpdateSelf, OpenACC_UpdateDevice, OpenACC_UseDevice,
|
|
OpenACC_Reduction, OpenACC_DeclareDeviceResident, OpenACC_DeclareLink,
|
|
OpenACC_Cache, OpenACC_CacheReadonly,
|
|
]> {
|
|
let cppNamespace = "::mlir::acc";
|
|
let genSpecializedAttr = 0;
|
|
}
|
|
|
|
def OpenACC_DataClauseAttr : EnumAttr<OpenACC_Dialect, OpenACC_DataClauseEnum,
|
|
"data_clause">;
|
|
|
|
// Data clause modifiers:
|
|
// * readonly: Added in OpenACC 2.7 to copyin and cache.
|
|
// * zero: Added in OpenACC 3.0 for create and copyout.
|
|
// * always, alwaysin, alwaysout: Added in OpenACC 3.4 for
|
|
// copy, copyin, and copyout clauses.
|
|
// * capture: Added in OpenACC 3.4 for copy, copyin, copyout and create clauses.
|
|
def OpenACC_DataClauseModifierNone : I32BitEnumAttrCaseNone<"none">;
|
|
// All of the modifiers below are bit flags - so the value noted is `1 << bit`.
|
|
// Thus the `zero` modifier is `1 << 0` = 1, `readonly` is `1 << 1` = 2, etc.
|
|
def OpenACC_DataClauseModifierZero : I32BitEnumAttrCaseBit<"zero", 0>;
|
|
def OpenACC_DataClauseModifierReadonly : I32BitEnumAttrCaseBit<"readonly", 1>;
|
|
def OpenACC_DataClauseModifierAlwaysIn : I32BitEnumAttrCaseBit<"alwaysin", 2>;
|
|
def OpenACC_DataClauseModifierAlwaysOut : I32BitEnumAttrCaseBit<"alwaysout", 3>;
|
|
def OpenACC_DataClauseModifierAlways : I32BitEnumAttrCaseGroup<"always",
|
|
[OpenACC_DataClauseModifierAlwaysIn, OpenACC_DataClauseModifierAlwaysOut]>;
|
|
def OpenACC_DataClauseModifierCapture : I32BitEnumAttrCaseBit<"capture", 4>;
|
|
|
|
def OpenACC_DataClauseModifierEnum : I32BitEnumAttr<
|
|
"DataClauseModifier",
|
|
"Captures data clause modifiers",
|
|
[
|
|
OpenACC_DataClauseModifierNone, OpenACC_DataClauseModifierZero,
|
|
OpenACC_DataClauseModifierReadonly, OpenACC_DataClauseModifierAlwaysIn,
|
|
OpenACC_DataClauseModifierAlwaysOut, OpenACC_DataClauseModifierAlways,
|
|
OpenACC_DataClauseModifierCapture]> {
|
|
let separator = ",";
|
|
let cppNamespace = "::mlir::acc";
|
|
let genSpecializedAttr = 0;
|
|
let printBitEnumPrimaryGroups = 1;
|
|
}
|
|
|
|
def OpenACC_DataClauseModifierAttr : EnumAttr<OpenACC_Dialect,
|
|
OpenACC_DataClauseModifierEnum,
|
|
"data_clause_modifier">;
|
|
|
|
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,
|
|
DefaultValuedParameter<"bool", "false">:$implicit);
|
|
let assemblyFormat = "`<` struct(params) `>`";
|
|
let builders = [AttrBuilder<(ins "DataClauseAttr":$dataClause), [{
|
|
return $_get($_ctxt, dataClause, /*implicit=*/false);
|
|
}]>
|
|
];
|
|
}
|
|
|
|
// Attribute to attach functions that perform the pre/post allocation actions or
|
|
// pre/post deallocation actions as described in section 2.13.
|
|
def DeclareActionAttr : OpenACC_Attr<"DeclareAction", "declare_action"> {
|
|
let parameters = (ins OptionalParameter<"SymbolRefAttr">:$preAlloc,
|
|
OptionalParameter<"SymbolRefAttr">:$postAlloc,
|
|
OptionalParameter<"SymbolRefAttr">:$preDealloc,
|
|
OptionalParameter<"SymbolRefAttr">:$postDealloc);
|
|
let assemblyFormat = "`<` struct(params) `>`";
|
|
}
|
|
|
|
// Device type enumeration.
|
|
def OpenACC_DeviceTypeNone : I32EnumAttrCase<"None", 0, "none">;
|
|
def OpenACC_DeviceTypeStar : I32EnumAttrCase<"Star", 1, "star">;
|
|
def OpenACC_DeviceTypeDefault : I32EnumAttrCase<"Default", 2, "default">;
|
|
def OpenACC_DeviceTypeHost : I32EnumAttrCase<"Host", 3, "host">;
|
|
def OpenACC_DeviceTypeMulticore : I32EnumAttrCase<"Multicore", 4, "multicore">;
|
|
def OpenACC_DeviceTypeNvidia : I32EnumAttrCase<"Nvidia", 5, "nvidia">;
|
|
def OpenACC_DeviceTypeRadeon : I32EnumAttrCase<"Radeon", 6, "radeon">;
|
|
|
|
def OpenACC_DeviceType : I32EnumAttr<"DeviceType",
|
|
"built-in device type supported by OpenACC",
|
|
[OpenACC_DeviceTypeNone, OpenACC_DeviceTypeStar, OpenACC_DeviceTypeDefault,
|
|
OpenACC_DeviceTypeHost, OpenACC_DeviceTypeMulticore,
|
|
OpenACC_DeviceTypeNvidia, OpenACC_DeviceTypeRadeon
|
|
]> {
|
|
let genSpecializedAttr = 0;
|
|
let cppNamespace = "::mlir::acc";
|
|
}
|
|
|
|
// Device type attribute is used to associate a value for for clauses that
|
|
// appear after a device_type clause. The list of clauses allowed after the
|
|
// device_type clause is defined per construct as follows:
|
|
// Loop construct: collapse, gang, worker, vector, seq, independent, auto,
|
|
// and tile
|
|
// Compute construct: async, wait, num_gangs, num_workers, and vector_length
|
|
// Data construct: async and wait
|
|
// Routine: gang, worker, vector, seq and bind
|
|
//
|
|
// The `none` means that the value appears before any device_type clause.
|
|
//
|
|
def OpenACC_DeviceTypeAttr : EnumAttr<OpenACC_Dialect,
|
|
OpenACC_DeviceType,
|
|
"device_type"> {
|
|
let assemblyFormat = [{ ```<` $value `>` }];
|
|
}
|
|
|
|
def DeviceTypeArrayAttr :
|
|
TypedArrayAttrBase<OpenACC_DeviceTypeAttr, "device type array attribute"> {
|
|
let constBuilderCall = ?;
|
|
}
|
|
|
|
// Gang arg type enumeration
|
|
def OpenACC_GangArgNum : I32EnumAttrCase<"Num", 0, "Num">;
|
|
def OpenACC_GangArgDim : I32EnumAttrCase<"Dim", 1, "Dim">;
|
|
def OpenACC_GangArgStatic : I32EnumAttrCase<"Static", 2, "Static">;
|
|
|
|
def OpenACC_GangArgType : I32EnumAttr<"GangArgType",
|
|
"Differentiate the different gang arg values",
|
|
[OpenACC_GangArgNum, OpenACC_GangArgDim, OpenACC_GangArgStatic]> {
|
|
let genSpecializedAttr = 0;
|
|
let cppNamespace = "::mlir::acc";
|
|
}
|
|
def OpenACC_GangArgTypeAttr : EnumAttr<OpenACC_Dialect,
|
|
OpenACC_GangArgType,
|
|
"gang_arg_type"> {
|
|
let assemblyFormat = [{ ```<` $value `>` }];
|
|
}
|
|
def GangArgTypeArrayAttr :
|
|
TypedArrayAttrBase<OpenACC_GangArgTypeAttr, "gang arg type array attribute"> {
|
|
let constBuilderCall = ?;
|
|
}
|
|
|
|
// Combined constructs enumerations
|
|
def OpenACC_KernelsLoop : I32EnumAttrCase<"KernelsLoop", 1, "kernels_loop">;
|
|
def OpenACC_ParallelLoop : I32EnumAttrCase<"ParallelLoop", 2, "parallel_loop">;
|
|
def OpenACC_SerialLoop : I32EnumAttrCase<"SerialLoop", 3, "serial_loop">;
|
|
|
|
def OpenACC_CombinedConstructsType : I32EnumAttr<"CombinedConstructsType",
|
|
"Differentiate between combined constructs",
|
|
[OpenACC_KernelsLoop, OpenACC_ParallelLoop, OpenACC_SerialLoop]> {
|
|
let genSpecializedAttr = 0;
|
|
let cppNamespace = "::mlir::acc";
|
|
}
|
|
|
|
def OpenACC_CombinedConstructsAttr : EnumAttr<OpenACC_Dialect,
|
|
OpenACC_CombinedConstructsType,
|
|
"combined_constructs"> {
|
|
let assemblyFormat = [{ ```<` $value `>` }];
|
|
}
|
|
|
|
def OpenACC_ParallelConstruct : I64EnumAttrCase<"acc_construct_parallel", 0>;
|
|
def OpenACC_KernelsConstruct : I64EnumAttrCase<"acc_construct_kernels", 1>;
|
|
def OpenACC_LoopConstruct : I64EnumAttrCase<"acc_construct_loop", 2>;
|
|
def OpenACC_DataConstruct : I64EnumAttrCase<"acc_construct_data", 3>;
|
|
def OpenACC_EnterDataConstruct : I64EnumAttrCase<"acc_construct_enter_data", 4>;
|
|
def OpenACC_ExitDataConstruct : I64EnumAttrCase<"acc_construct_exit_data", 5>;
|
|
def OpenACC_HostDataConstruct : I64EnumAttrCase<"acc_construct_host_data", 6>;
|
|
def OpenACC_AtomicConstruct : I64EnumAttrCase<"acc_construct_atomic", 7>;
|
|
def OpenACC_DeclareConstruct : I64EnumAttrCase<"acc_construct_declare", 8>;
|
|
def OpenACC_InitConstruct : I64EnumAttrCase<"acc_construct_init", 9>;
|
|
def OpenACC_ShutdownConstruct : I64EnumAttrCase<"acc_construct_shutdown", 10>;
|
|
def OpenACC_SetConstruct : I64EnumAttrCase<"acc_construct_set", 11>;
|
|
def OpenACC_UpdateConstruct : I64EnumAttrCase<"acc_construct_update", 12>;
|
|
def OpenACC_RoutineConstruct : I64EnumAttrCase<"acc_construct_routine", 13>;
|
|
def OpenACC_WaitConstruct : I64EnumAttrCase<"acc_construct_wait", 14>;
|
|
def OpenACC_RuntimeAPIConstruct : I64EnumAttrCase<"acc_construct_runtime_api", 15>;
|
|
def OpenACC_SerialConstruct : I64EnumAttrCase<"acc_construct_serial", 16>;
|
|
|
|
def OpenACC_ConstructEnum : I64EnumAttr<"Construct",
|
|
"constructs supported by OpenACC",
|
|
[OpenACC_ParallelConstruct, OpenACC_KernelsConstruct,
|
|
OpenACC_LoopConstruct, OpenACC_DataConstruct,
|
|
OpenACC_EnterDataConstruct, OpenACC_ExitDataConstruct,
|
|
OpenACC_HostDataConstruct, OpenACC_AtomicConstruct,
|
|
OpenACC_DeclareConstruct, OpenACC_InitConstruct,
|
|
OpenACC_ShutdownConstruct, OpenACC_SetConstruct,
|
|
OpenACC_UpdateConstruct, OpenACC_RoutineConstruct,
|
|
OpenACC_WaitConstruct, OpenACC_RuntimeAPIConstruct,
|
|
OpenACC_SerialConstruct
|
|
]> {
|
|
let genSpecializedAttr = 0;
|
|
let cppNamespace = "::mlir::acc";
|
|
}
|
|
|
|
def OpenACC_ConstructAttr : EnumAttr<OpenACC_Dialect, OpenACC_ConstructEnum,
|
|
"construct">;
|
|
|
|
// Define a resource for the OpenACC runtime counters.
|
|
def OpenACC_RuntimeCounters : Resource<"::mlir::acc::RuntimeCounters">;
|
|
|
|
// Define a resource for the OpenACC constructs.
|
|
// Useful to ensure that the constructs are not removed (even though
|
|
// the data semantics are encoded in the operations linked via their
|
|
// `dataOperands` list).
|
|
def OpenACC_ConstructResource : Resource<"::mlir::acc::ConstructResource">;
|
|
|
|
// Define a resource for the OpenACC current device setting.
|
|
def OpenACC_CurrentDeviceIdResource : Resource<"::mlir::acc::CurrentDeviceIdResource">;
|
|
|
|
// Attribute for saving variable names - this can be attached to non-acc-dialect
|
|
// operations in order to ensure the name is preserved.
|
|
def OpenACC_VarNameAttr : OpenACC_Attr<"VarName", "var_name"> {
|
|
let parameters = (ins StringRefParameter<"">:$name);
|
|
let assemblyFormat = "`<` $name `>`";
|
|
}
|
|
|
|
// 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,
|
|
OutlineRematerializationOpInterface]> {
|
|
let summary = "Represents normalized bounds information for acc data clause.";
|
|
|
|
let description = [{
|
|
This operation is used to record bounds used in acc data clause in a
|
|
normalized fashion (zero-based). This works well with the `PointerLikeType`
|
|
requirement in data clauses - since a `lowerbound` of 0 means looking
|
|
at data at the zero offset from pointer.
|
|
|
|
The operation must have an `upperbound` or `extent` (or both are allowed -
|
|
but not checked for consistency). When the source language's arrays are
|
|
not zero-based, the `startIdx` must specify the zero-position index.
|
|
|
|
The `stride` represents the distance between consecutive elements. For
|
|
multi-dimensional arrays, the `stride` for each outer dimension must account
|
|
for the complete size of all inner dimensions.
|
|
|
|
The `strideInBytes` flag indicates that the `stride` is specified in bytes
|
|
rather than the number of elements.
|
|
|
|
Examples below show copying a slice of 10-element array except first element.
|
|
Note that the examples use extent in data clause for C++ and upperbound
|
|
for Fortran (as per 2.7.1). To simplify examples, the constants are used
|
|
directly in the acc.bounds operands - this is not the syntax of operation.
|
|
|
|
C++:
|
|
```
|
|
int array[10];
|
|
#pragma acc copy(array[1:9])
|
|
```
|
|
=>
|
|
```mlir
|
|
acc.bounds lb(1) ub(9) extent(9) startIdx(0) stride(1)
|
|
```
|
|
|
|
Fortran:
|
|
```
|
|
integer :: array(1:10)
|
|
!$acc copy(array(2:10))
|
|
```
|
|
=>
|
|
```mlir
|
|
acc.bounds lb(1) ub(9) extent(9) startIdx(1) stride(1)
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins Optional<IntOrIndex>:$lowerbound,
|
|
Optional<IntOrIndex>:$upperbound,
|
|
Optional<IntOrIndex>:$extent,
|
|
Optional<IntOrIndex>:$stride,
|
|
DefaultValuedAttr<BoolAttr, "false">:$strideInBytes,
|
|
Optional<IntOrIndex>:$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;
|
|
|
|
let builders = [
|
|
OpBuilder<(ins "::mlir::Value":$extent), [{
|
|
build($_builder, $_state,
|
|
::mlir::acc::DataBoundsType::get($_builder.getContext()),
|
|
/*lowerbound=*/{}, /*upperbound=*/{}, extent,
|
|
/*stride=*/{}, /*strideInBytes=*/$_builder.getBoolAttr(false),
|
|
/*startIdx=*/{});
|
|
}]
|
|
>,
|
|
OpBuilder<(ins "::mlir::Value":$lowerbound,
|
|
"::mlir::Value":$upperbound), [{
|
|
build($_builder, $_state,
|
|
::mlir::acc::DataBoundsType::get($_builder.getContext()),
|
|
lowerbound, upperbound, /*extent=*/{},
|
|
/*stride=*/{}, /*strideInBytes=*/$_builder.getBoolAttr(false),
|
|
/*startIdx=*/{});
|
|
}]
|
|
>
|
|
];
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// DataBounds accessor operations
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_GetLowerboundOp : OpenACC_Op<"get_lowerbound", [NoMemoryEffect]> {
|
|
let summary = "Extract lowerbound from OpenACC data bounds.";
|
|
let description = [{
|
|
This operation extracts the lowerbound value from an `acc.bounds` value.
|
|
If the data bounds does not have a lowerbound specified, it means it is zero.
|
|
|
|
Example:
|
|
```mlir
|
|
%lb = acc.get_lowerbound %bounds : (!acc.data_bounds_ty) -> index
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins OpenACC_DataBoundsType:$bounds);
|
|
let results = (outs Index:$result);
|
|
|
|
let assemblyFormat = "$bounds attr-dict `:` `(` type($bounds) `)` `->` type($result)";
|
|
}
|
|
|
|
def OpenACC_GetUpperboundOp : OpenACC_Op<"get_upperbound", [NoMemoryEffect]> {
|
|
let summary = "Extract upperbound from OpenACC data bounds.";
|
|
let description = [{
|
|
This operation extracts the upperbound value from an `acc.bounds` value.
|
|
If the data bounds does not have an upperbound specified, this operation
|
|
uses the extent to compute it.
|
|
|
|
Example:
|
|
```mlir
|
|
%ub = acc.get_upperbound %bounds : (!acc.data_bounds_ty) -> index
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins OpenACC_DataBoundsType:$bounds);
|
|
let results = (outs Index:$result);
|
|
|
|
let assemblyFormat = "$bounds attr-dict `:` `(` type($bounds) `)` `->` type($result)";
|
|
}
|
|
|
|
def OpenACC_GetStrideOp : OpenACC_Op<"get_stride", [NoMemoryEffect]> {
|
|
let summary = "Extract stride from OpenACC data bounds.";
|
|
let description = [{
|
|
This operation extracts the stride value from an `acc.bounds` value.
|
|
If the data bounds does not have a stride specified, it defaults to 1.
|
|
|
|
Example:
|
|
```mlir
|
|
%stride = acc.get_stride %bounds : (!acc.data_bounds_ty) -> index
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins OpenACC_DataBoundsType:$bounds);
|
|
let results = (outs Index:$result);
|
|
|
|
let assemblyFormat = "$bounds attr-dict `:` `(` type($bounds) `)` `->` type($result)";
|
|
}
|
|
|
|
def OpenACC_GetExtentOp : OpenACC_Op<"get_extent", [NoMemoryEffect]> {
|
|
let summary = "Extract extent from OpenACC data bounds.";
|
|
let description = [{
|
|
This operation extracts the extent value from an `acc.bounds` value.
|
|
If the data bounds does not have an extent specified, it is computed
|
|
from the upperbound.
|
|
|
|
Example:
|
|
```mlir
|
|
%extent = acc.get_extent %bounds : (!acc.data_bounds_ty) -> index
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins OpenACC_DataBoundsType:$bounds);
|
|
let results = (outs Index:$result);
|
|
|
|
let assemblyFormat = "$bounds attr-dict `:` `(` type($bounds) `)` `->` type($result)";
|
|
}
|
|
|
|
// 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,
|
|
string extraDescription, list<Trait> traits = [],
|
|
dag additionalArgs = (ins)>
|
|
: OpenACC_Op<mnemonic,
|
|
!listconcat(traits, [AttrSizedOperandSegments,
|
|
DeclareOpInterfaceMethods<
|
|
MemoryEffectsOpInterface>])> {
|
|
let arguments = !con(
|
|
additionalArgs,
|
|
(ins TypeAttr:$varType,
|
|
Optional<OpenACC_PointerLikeTypeInterface>:$varPtrPtr,
|
|
Variadic<OpenACC_DataBoundsType>:$bounds, /* rank-0 to rank-{n-1} */
|
|
Variadic<IntOrIndex>:$asyncOperands,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOperandsDeviceType,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOnly,
|
|
DefaultValuedAttr<OpenACC_DataClauseAttr, clause>:$dataClause,
|
|
DefaultValuedAttr<BoolAttr, "true">:$structured,
|
|
DefaultValuedAttr<BoolAttr, "false">:$implicit,
|
|
DefaultValuedAttr<OpenACC_DataClauseModifierAttr,
|
|
"mlir::acc::DataClauseModifier::none">:$modifiers,
|
|
OptionalAttr<StrAttr>:$name,
|
|
OptionalAttr<SymbolRefAttr>:$recipe));
|
|
|
|
let description = !strconcat(extraDescription, [{
|
|
Description of arguments:
|
|
- `var`: The variable to copy. Must be either `MappableType` or
|
|
`PointerLikeType`.
|
|
- `varType`: The type of the variable that is being copied. When `var` is
|
|
a `MappableType`, this matches the type of `var`. When `var` is a
|
|
`PointerLikeType`, this type holds information about the target of the
|
|
pointer.
|
|
- `varPtrPtr`: Specifies the address of the address of `var` - only used
|
|
when the variable copied is a field in a struct. This is important for
|
|
OpenACC due to implicit attach semantics on data clauses (2.6.4).
|
|
- `bounds`: Used when copying just slice of array or array's bounds are not
|
|
encoded in type. They are in rank order where rank 0 is inner-most dimension.
|
|
- `asyncOperands` and `asyncOperandsDeviceType`:
|
|
pair-wise lists of the async clause values associated with device_type's.
|
|
- `asyncOnly`: a list of device_type's for which async clause
|
|
does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1).
|
|
- `dataClause`: Keeps track of the data clause the user used. This is because
|
|
the acc operations are decomposed. So a 'copy' clause is decomposed to both
|
|
`acc.copyin` and `acc.copyout` operations, but both have dataClause that
|
|
specifies `acc_copy` in this field.
|
|
- `structured`: Flag to note whether this is associated with structured region
|
|
(parallel, kernels, data) or unstructured (enter data, exit data). This is
|
|
important due to spec specifically calling out structured and dynamic reference
|
|
counters (2.6.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.
|
|
- `modifiers`: Keeps track of the data clause modifiers (eg zero, readonly, etc)
|
|
- `name`: Holds the name of variable as specified in user clause (including bounds).
|
|
|
|
The async values attached to the data entry operation imply that the data
|
|
action applies to all device types specified by the device_type clauses
|
|
using the activity queues on these devices as defined by the async values.
|
|
}]);
|
|
|
|
code extraClassDeclarationBase = [{
|
|
/// Return true if the op has the async attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasAsyncOnly() {
|
|
return hasAsyncOnly(mlir::acc::DeviceType::None);
|
|
}
|
|
/// Return true if the op has the async attribute for the given device_type.
|
|
bool hasAsyncOnly(mlir::acc::DeviceType deviceType) {
|
|
mlir::ArrayAttr asyncOnly = getAsyncOnlyAttr();
|
|
if (!asyncOnly)
|
|
return false;
|
|
for (auto attr : asyncOnly) {
|
|
auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
|
|
if (deviceTypeAttr.getValue() == deviceType)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
/// Return the value of the async clause if present.
|
|
mlir::Value getAsyncValue() {
|
|
return getAsyncValue(mlir::acc::DeviceType::None);
|
|
}
|
|
/// Return the value of the async clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getAsyncValue(mlir::acc::DeviceType deviceType) {
|
|
mlir::ArrayAttr deviceTypes = getAsyncOperandsDeviceTypeAttr();
|
|
if (!deviceTypes)
|
|
return nullptr;
|
|
for (auto [attr, asyncValue] :
|
|
llvm::zip(deviceTypes, getAsyncOperands())) {
|
|
auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
|
|
if (deviceTypeAttr.getValue() == deviceType)
|
|
return asyncValue;
|
|
}
|
|
return nullptr;
|
|
}
|
|
mlir::TypedValue<mlir::acc::PointerLikeType> getVarPtr() {
|
|
return mlir::dyn_cast<mlir::TypedValue<mlir::acc::PointerLikeType>>(getVar());
|
|
}
|
|
mlir::TypedValue<mlir::acc::PointerLikeType> getAccPtr() {
|
|
return mlir::dyn_cast<mlir::TypedValue<mlir::acc::PointerLikeType>>(getAccVar());
|
|
}
|
|
}];
|
|
|
|
let assemblyFormat = [{
|
|
custom<Var>($var) `:` custom<VarPtrType>(type($var), $varType)
|
|
oilist(
|
|
`varPtrPtr` `(` $varPtrPtr `:` type($varPtrPtr) `)`
|
|
| `bounds` `(` $bounds `)`
|
|
| `async` `` custom<DeviceTypeOperandsWithKeywordOnly>($asyncOperands,
|
|
type($asyncOperands), $asyncOperandsDeviceType, $asyncOnly)
|
|
| `recipe` `(` custom<RecipeSym>($recipe) `)`
|
|
) `->` type($accVar) attr-dict
|
|
}];
|
|
|
|
let hasVerifier = 1;
|
|
|
|
let builders = [
|
|
OpBuilder<(ins "::mlir::Value":$var,
|
|
"bool":$structured, "bool":$implicit,
|
|
CArg<"::mlir::ValueRange", "{}">:$bounds),
|
|
[{
|
|
auto ptrLikeTy = ::mlir::dyn_cast<::mlir::acc::PointerLikeType>(
|
|
var.getType());
|
|
build($_builder, $_state, var.getType(), var,
|
|
/*varType=*/ptrLikeTy ?
|
|
::mlir::TypeAttr::get(ptrLikeTy.getElementType()) :
|
|
::mlir::TypeAttr::get(var.getType()),
|
|
/*varPtrPtr=*/{}, bounds, /*asyncOperands=*/{},
|
|
/*asyncOperandsDeviceType=*/nullptr,
|
|
/*asyncOnly=*/nullptr, /*dataClause=*/nullptr,
|
|
/*structured=*/$_builder.getBoolAttr(structured),
|
|
/*implicit=*/$_builder.getBoolAttr(implicit), /*modifiers=*/nullptr,
|
|
/*name=*/nullptr, /*recipe=*/nullptr);
|
|
}]>,
|
|
OpBuilder<(ins "::mlir::Value":$var,
|
|
"bool":$structured, "bool":$implicit,
|
|
"const ::llvm::Twine &":$name,
|
|
CArg<"::mlir::ValueRange", "{}">:$bounds),
|
|
[{
|
|
auto ptrLikeTy = ::mlir::dyn_cast<::mlir::acc::PointerLikeType>(
|
|
var.getType());
|
|
build($_builder, $_state, var.getType(), var,
|
|
/*varType=*/ptrLikeTy ?
|
|
::mlir::TypeAttr::get(ptrLikeTy.getElementType()) :
|
|
::mlir::TypeAttr::get(var.getType()),
|
|
/*varPtrPtr=*/{}, bounds, /*asyncOperands=*/{},
|
|
/*asyncOperandsDeviceType=*/nullptr,
|
|
/*asyncOnly=*/nullptr, /*dataClause=*/nullptr,
|
|
/*structured=*/$_builder.getBoolAttr(structured),
|
|
/*implicit=*/$_builder.getBoolAttr(implicit), /*modifiers=*/nullptr,
|
|
/*name=*/$_builder.getStringAttr(name), /*recipe=*/nullptr);
|
|
}]>,
|
|
OpBuilder<(ins "::mlir::Type":$accVarType, "::mlir::Value":$var,
|
|
"::mlir::Type":$varType, "::mlir::Value":$varPtrPtr,
|
|
"::mlir::ValueRange":$bounds,
|
|
"::mlir::ValueRange":$asyncOperands,
|
|
"::mlir::ArrayAttr":$asyncOperandsDeviceType,
|
|
"::mlir::ArrayAttr":$asyncOnly,
|
|
"::mlir::acc::DataClause":$dataClause, "bool":$structured,
|
|
"bool":$implicit, "::mlir::StringAttr":$name),
|
|
[{
|
|
// Builder provided to ease transition for new data clause modifiers operand.
|
|
build($_builder, $_state, accVarType, var, varType, varPtrPtr, bounds,
|
|
asyncOperands, asyncOperandsDeviceType, asyncOnly, dataClause,
|
|
structured, implicit, ::mlir::acc::DataClauseModifier::none, name);
|
|
}]>,
|
|
OpBuilder<(ins "::mlir::Type":$accVarType, "::mlir::Value":$var,
|
|
"::mlir::Type":$varType, "::mlir::Value":$varPtrPtr,
|
|
"::mlir::ValueRange":$bounds,
|
|
"::mlir::ValueRange":$asyncOperands,
|
|
"::mlir::ArrayAttr":$asyncOperandsDeviceType,
|
|
"::mlir::ArrayAttr":$asyncOnly,
|
|
"::mlir::acc::DataClause":$dataClause, "bool":$structured,
|
|
"bool":$implicit,
|
|
"::mlir::acc::DataClauseModifier":$modifiers,
|
|
"::mlir::StringAttr":$name),
|
|
[{
|
|
// Builder provided to simplify building after recipe operand was added.
|
|
build($_builder, $_state, accVarType, var, varType, varPtrPtr, bounds,
|
|
asyncOperands, asyncOperandsDeviceType, asyncOnly, dataClause,
|
|
structured, implicit, modifiers, name, /*recipe=*/nullptr);
|
|
}]>,
|
|
];
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.5.13 private clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_PrivateOp : OpenACC_DataEntryOp<"private",
|
|
"mlir::acc::DataClause::acc_private", "", [],
|
|
(ins OpenACC_AnyPointerOrMappableType:$var)> {
|
|
let summary = "Represents private semantics for acc private clause.";
|
|
let results = (outs Arg<OpenACC_AnyPointerOrMappableType,
|
|
"Accelerator mapped variable">:$accVar);
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.5.14 firstprivate clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_FirstprivateOp
|
|
: OpenACC_DataEntryOp<
|
|
"firstprivate", "mlir::acc::DataClause::acc_firstprivate", "", [],
|
|
(ins Arg<OpenACC_AnyPointerOrMappableType, "Host variable">:$var)> {
|
|
let summary = "Represents firstprivate semantic for the acc firstprivate "
|
|
"clause.";
|
|
let results = (outs Arg<OpenACC_AnyPointerOrMappableType,
|
|
"Accelerator mapped variable">:$accVar);
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.5.15 reduction clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_ReductionOp
|
|
: OpenACC_DataEntryOp<
|
|
"reduction", "mlir::acc::DataClause::acc_reduction", "", [],
|
|
(ins Arg<OpenACC_AnyPointerOrMappableType, "Host variable">:$var)> {
|
|
let summary = "Represents reduction semantics for acc reduction clause.";
|
|
let results = (outs Arg<OpenACC_AnyPointerOrMappableType,
|
|
"Accelerator mapped variable">:$accVar);
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.7.4 deviceptr clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_DevicePtrOp
|
|
: OpenACC_DataEntryOp<"deviceptr", "mlir::acc::DataClause::acc_deviceptr",
|
|
"", [], (ins OpenACC_AnyPointerOrMappableType:$var)> {
|
|
let summary = "Specifies that the variable pointer is a device pointer.";
|
|
let results = (outs OpenACC_AnyPointerOrMappableType:$accVar);
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.7.5 present clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_PresentOp
|
|
: OpenACC_DataEntryOp<"present", "mlir::acc::DataClause::acc_present",
|
|
"", [], (ins OpenACC_AnyPointerOrMappableType:$var)> {
|
|
let summary = "Specifies that the variable is already present on device.";
|
|
let results = (outs OpenACC_AnyPointerOrMappableType:$accVar);
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.7.7 copyin clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_CopyinOp
|
|
: OpenACC_DataEntryOp<
|
|
"copyin", "mlir::acc::DataClause::acc_copyin", "", [],
|
|
(ins Arg<OpenACC_AnyPointerOrMappableType, "Host variable">:$var)> {
|
|
let summary = "Represents copyin semantics for acc data clauses like acc "
|
|
"copyin and acc copy.";
|
|
let results = (outs Arg<OpenACC_AnyPointerOrMappableType,
|
|
"Accelerator mapped variable">:$accVar);
|
|
|
|
let extraClassDeclaration = extraClassDeclarationBase # [{
|
|
/// 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", "", [],
|
|
(ins OpenACC_AnyPointerOrMappableType:$var)> {
|
|
let summary = "Represents create semantics for acc data clauses like acc "
|
|
"create and acc copyout.";
|
|
let results = (outs Arg<OpenACC_AnyPointerOrMappableType,
|
|
"Accelerator mapped variable">:$accVar);
|
|
|
|
let extraClassDeclaration = extraClassDeclarationBase # [{
|
|
/// 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",
|
|
"", [], (ins OpenACC_AnyPointerOrMappableType:$var)> {
|
|
let summary = "Represents acc no_create semantics.";
|
|
let results = (outs OpenACC_AnyPointerOrMappableType:$accVar);
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.7.12 attach clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_AttachOp
|
|
: OpenACC_DataEntryOp<
|
|
"attach", "mlir::acc::DataClause::acc_attach", "", [],
|
|
(ins Arg<OpenACC_AnyPointerOrMappableType, "Host variable">:$var)> {
|
|
let summary = "Represents acc attach semantics which updates a pointer in "
|
|
"device memory with the corresponding device address of the "
|
|
"pointee.";
|
|
let results = (outs OpenACC_AnyPointerOrMappableType:$accVar);
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 3.2.23 acc_deviceptr
|
|
//===----------------------------------------------------------------------===//
|
|
// This is needed to get device address without the additional semantics in
|
|
// 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", [{
|
|
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.
|
|
\
|
|
}],
|
|
[], (ins OpenACC_AnyPointerOrMappableType:$var)> {
|
|
let summary = "Gets device address if variable exists on device.";
|
|
let results = (outs OpenACC_AnyPointerOrMappableType:$accVar);
|
|
let hasVerifier = 0;
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.14.4 device clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_UpdateDeviceOp
|
|
: OpenACC_DataEntryOp<
|
|
"update_device", "mlir::acc::DataClause::acc_update_device", "", [],
|
|
(ins Arg<OpenACC_AnyPointerOrMappableType, "Host variable">:$var)> {
|
|
let summary = "Represents acc update device semantics.";
|
|
let results = (outs Arg<OpenACC_AnyPointerOrMappableType,
|
|
"Accelerator mapped variable">:$accVar);
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.8 use_device clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_UseDeviceOp
|
|
: OpenACC_DataEntryOp<"use_device", "mlir::acc::DataClause::acc_use_device",
|
|
"", [], (ins OpenACC_AnyPointerOrMappableType:$var)> {
|
|
let summary = "Represents acc use_device semantics.";
|
|
let results = (outs OpenACC_AnyPointerOrMappableType:$accVar);
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.13.1 device_resident clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_DeclareDeviceResidentOp
|
|
: OpenACC_DataEntryOp<
|
|
"declare_device_resident",
|
|
"mlir::acc::DataClause::acc_declare_device_resident", "", [],
|
|
(ins Arg<OpenACC_AnyPointerOrMappableType, "Host variable">:$var)> {
|
|
let summary = "Represents acc declare device_resident semantics.";
|
|
let results = (outs OpenACC_AnyPointerOrMappableType:$accVar);
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.13.3 link clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_DeclareLinkOp
|
|
: OpenACC_DataEntryOp<
|
|
"declare_link", "mlir::acc::DataClause::acc_declare_link", "", [],
|
|
(ins Arg<OpenACC_AnyPointerOrMappableType, "Host variable">:$var)> {
|
|
let summary = "Represents acc declare link semantics.";
|
|
let results = (outs OpenACC_AnyPointerOrMappableType:$accVar);
|
|
let extraClassDeclaration = extraClassDeclarationBase;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.10 cache directive
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_CacheOp
|
|
: OpenACC_DataEntryOp<"cache", "mlir::acc::DataClause::acc_cache", "", [],
|
|
(ins OpenACC_AnyPointerOrMappableType:$var)> {
|
|
let summary = "Represents the cache directive that is associated with a "
|
|
"loop.";
|
|
let results = (outs OpenACC_AnyPointerOrMappableType:$accVar);
|
|
|
|
let extraClassDeclaration = extraClassDeclarationBase # [{
|
|
/// Check if this is a cache with readonly modifier.
|
|
bool isCacheReadonly();
|
|
}];
|
|
}
|
|
|
|
// 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,
|
|
string extraDescription, list<Trait> traits = [],
|
|
dag additionalArgs = (ins)>
|
|
: OpenACC_Op<mnemonic,
|
|
!listconcat(traits, [AttrSizedOperandSegments,
|
|
DeclareOpInterfaceMethods<
|
|
MemoryEffectsOpInterface>])> {
|
|
let arguments = !con(additionalArgs,
|
|
(ins Variadic<OpenACC_DataBoundsType>:$bounds,
|
|
Variadic<IntOrIndex>:$asyncOperands,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOperandsDeviceType,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOnly,
|
|
DefaultValuedAttr<OpenACC_DataClauseAttr,clause>:$dataClause,
|
|
DefaultValuedAttr<BoolAttr, "true">:$structured,
|
|
DefaultValuedAttr<BoolAttr, "false">:$implicit,
|
|
DefaultValuedAttr<OpenACC_DataClauseModifierAttr,
|
|
"mlir::acc::DataClauseModifier::none">:$modifiers,
|
|
OptionalAttr<StrAttr>:$name));
|
|
|
|
let description = !strconcat(extraDescription, [{
|
|
- `accVar`: The acc variable. This is the link from the data-entry
|
|
operation used.
|
|
- `bounds`: Used when copying just slice of array or array's bounds are not
|
|
encoded in type. They are in rank order where rank 0 is inner-most dimension.
|
|
- `asyncOperands` and `asyncOperandsDeviceType`:
|
|
pair-wise lists of the async clause values associated with device_type's.
|
|
- `asyncOnly`: a list of device_type's for which async clause
|
|
does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1).
|
|
- `dataClause`: Keeps track of the data clause the user used. This is because
|
|
the acc operations are decomposed. So a 'copy' clause is decomposed to both
|
|
`acc.copyin` and `acc.copyout` operations, but both have dataClause that
|
|
specifies `acc_copy` in this field.
|
|
- `structured`: Flag to note whether this is associated with structured region
|
|
(parallel, kernels, data) or unstructured (enter data, exit data). This is
|
|
important due to spec specifically calling out structured and dynamic reference
|
|
counters (2.6.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.
|
|
- `modifiers`: Keeps track of the data clause modifiers (eg zero, always, etc)
|
|
- `name`: Holds the name of variable as specified in user clause (including bounds).
|
|
|
|
The async values attached to the data exit operation imply that the data
|
|
action applies to all device types specified by the device_type clauses
|
|
using the activity queues on these devices as defined by the async values.
|
|
}]);
|
|
|
|
code extraClassDeclarationBase = [{
|
|
/// Return true if the op has the async attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasAsyncOnly() {
|
|
return hasAsyncOnly(mlir::acc::DeviceType::None);
|
|
}
|
|
/// Return true if the op has the async attribute for the given device_type.
|
|
bool hasAsyncOnly(mlir::acc::DeviceType deviceType) {
|
|
mlir::ArrayAttr asyncOnly = getAsyncOnlyAttr();
|
|
if (!asyncOnly)
|
|
return false;
|
|
for (auto attr : asyncOnly) {
|
|
auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
|
|
if (deviceTypeAttr.getValue() == deviceType)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
/// Return the value of the async clause if present.
|
|
mlir::Value getAsyncValue() {
|
|
return getAsyncValue(mlir::acc::DeviceType::None);
|
|
}
|
|
/// Return the value of the async clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getAsyncValue(mlir::acc::DeviceType deviceType) {
|
|
mlir::ArrayAttr deviceTypes = getAsyncOperandsDeviceTypeAttr();
|
|
if (!deviceTypes)
|
|
return nullptr;
|
|
for (auto [attr, asyncValue] :
|
|
llvm::zip(deviceTypes, getAsyncOperands())) {
|
|
auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
|
|
if (deviceTypeAttr.getValue() == deviceType)
|
|
return asyncValue;
|
|
}
|
|
return nullptr;
|
|
}
|
|
}];
|
|
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
class OpenACC_DataExitOpWithVarPtr<string mnemonic, string clause>
|
|
: OpenACC_DataExitOp<
|
|
mnemonic, clause,
|
|
"- `varPtr`: The address of variable to copy back to.", [],
|
|
(ins Arg<OpenACC_AnyPointerOrMappableType,
|
|
"Accelerator mapped variable">:$accVar,
|
|
Arg<OpenACC_AnyPointerOrMappableType, "Host variable">:$var,
|
|
TypeAttr:$varType)> {
|
|
let assemblyFormat = [{
|
|
custom<AccVar>($accVar, type($accVar))
|
|
(`bounds` `(` $bounds^ `)` )?
|
|
(`async` `` custom<DeviceTypeOperandsWithKeywordOnly>($asyncOperands,
|
|
type($asyncOperands), $asyncOperandsDeviceType, $asyncOnly)^)?
|
|
`to` custom<Var>($var) `:` custom<VarPtrType>(type($var), $varType)
|
|
attr-dict
|
|
}];
|
|
|
|
let builders = [
|
|
OpBuilder<(ins "::mlir::Value":$accVar,
|
|
"::mlir::Value":$var,
|
|
"bool":$structured, "bool":$implicit,
|
|
CArg<"::mlir::ValueRange", "{}">:$bounds),
|
|
[{
|
|
auto ptrLikeTy = ::mlir::dyn_cast<::mlir::acc::PointerLikeType>(
|
|
var.getType());
|
|
build($_builder, $_state, accVar, var,
|
|
/*varType=*/ptrLikeTy ?
|
|
::mlir::TypeAttr::get(ptrLikeTy.getElementType()) :
|
|
::mlir::TypeAttr::get(var.getType()),
|
|
bounds, /*asyncOperands=*/{}, /*asyncOperandsDeviceType=*/nullptr,
|
|
/*asyncOnly=*/nullptr, /*dataClause=*/nullptr,
|
|
/*structured=*/$_builder.getBoolAttr(structured),
|
|
/*implicit=*/$_builder.getBoolAttr(implicit), /*modifiers=*/nullptr,
|
|
/*name=*/nullptr);
|
|
}]>,
|
|
OpBuilder<(ins "::mlir::Value":$accVar,
|
|
"::mlir::Value":$var,
|
|
"bool":$structured, "bool":$implicit,
|
|
"const ::llvm::Twine &":$name,
|
|
CArg<"::mlir::ValueRange", "{}">:$bounds),
|
|
[{
|
|
auto ptrLikeTy = ::mlir::dyn_cast<::mlir::acc::PointerLikeType>(
|
|
var.getType());
|
|
build($_builder, $_state, accVar, var,
|
|
/*varType=*/ptrLikeTy ?
|
|
::mlir::TypeAttr::get(ptrLikeTy.getElementType()) :
|
|
::mlir::TypeAttr::get(var.getType()),
|
|
bounds, /*asyncOperands=*/{}, /*asyncOperandsDeviceType=*/nullptr,
|
|
/*asyncOnly=*/nullptr, /*dataClause=*/nullptr,
|
|
/*structured=*/$_builder.getBoolAttr(structured),
|
|
/*implicit=*/$_builder.getBoolAttr(implicit), /*modifiers=*/nullptr,
|
|
/*name=*/$_builder.getStringAttr(name));
|
|
}]>,
|
|
OpBuilder<(ins "::mlir::Value":$accVar, "::mlir::Value":$var,
|
|
"::mlir::Type":$varType, "::mlir::ValueRange":$bounds,
|
|
"::mlir::ValueRange":$asyncOperands,
|
|
"::mlir::ArrayAttr":$asyncOperandsDeviceType,
|
|
"::mlir::ArrayAttr":$asyncOnly,
|
|
"::mlir::acc::DataClause":$dataClause, "bool":$structured,
|
|
"bool":$implicit, "::mlir::StringAttr":$name),
|
|
[{
|
|
build($_builder, $_state, accVar, var, varType, bounds,
|
|
asyncOperands, asyncOperandsDeviceType, asyncOnly, dataClause,
|
|
structured, implicit, ::mlir::acc::DataClauseModifier::none, name);
|
|
}]>,
|
|
];
|
|
|
|
code extraClassDeclarationDataExit = [{
|
|
mlir::TypedValue<mlir::acc::PointerLikeType> getVarPtr() {
|
|
return mlir::dyn_cast<mlir::TypedValue<mlir::acc::PointerLikeType>>(getVar());
|
|
}
|
|
mlir::TypedValue<mlir::acc::PointerLikeType> getAccPtr() {
|
|
return mlir::dyn_cast<mlir::TypedValue<mlir::acc::PointerLikeType>>(getAccVar());
|
|
}
|
|
}];
|
|
}
|
|
|
|
class OpenACC_DataExitOpNoVarPtr<string mnemonic, string clause>
|
|
: OpenACC_DataExitOp<mnemonic, clause, "", [],
|
|
(ins Arg<OpenACC_AnyPointerOrMappableType,
|
|
"Accelerator mapped variable">:$accVar)> {
|
|
let assemblyFormat = [{
|
|
custom<AccVar>($accVar, type($accVar))
|
|
(`bounds` `(` $bounds^ `)` )?
|
|
(`async` `` custom<DeviceTypeOperandsWithKeywordOnly>($asyncOperands,
|
|
type($asyncOperands), $asyncOperandsDeviceType, $asyncOnly)^)?
|
|
attr-dict
|
|
}];
|
|
|
|
let builders = [
|
|
OpBuilder<(ins "::mlir::Value":$accVar,
|
|
"bool":$structured, "bool":$implicit,
|
|
CArg<"::mlir::ValueRange", "{}">:$bounds),
|
|
[{
|
|
build($_builder, $_state, accVar,
|
|
bounds, /*asyncOperands=*/{}, /*asyncOperandsDeviceType=*/nullptr,
|
|
/*asyncOnly=*/nullptr, /*dataClause=*/nullptr,
|
|
/*structured=*/$_builder.getBoolAttr(structured),
|
|
/*implicit=*/$_builder.getBoolAttr(implicit), /*modifiers=*/nullptr,
|
|
/*name=*/nullptr);
|
|
}]>,
|
|
OpBuilder<(ins "::mlir::Value":$accVar,
|
|
"bool":$structured, "bool":$implicit,
|
|
"const ::llvm::Twine &":$name,
|
|
CArg<"::mlir::ValueRange", "{}">:$bounds),
|
|
[{
|
|
build($_builder, $_state, accVar,
|
|
bounds, /*asyncOperands=*/{}, /*asyncOperandsDeviceType=*/nullptr,
|
|
/*asyncOnly=*/nullptr, /*dataClause=*/nullptr,
|
|
/*structured=*/$_builder.getBoolAttr(structured),
|
|
/*implicit=*/$_builder.getBoolAttr(implicit), /*modifiers=*/nullptr,
|
|
/*name=*/$_builder.getStringAttr(name));
|
|
}]>,
|
|
OpBuilder<(ins "::mlir::Value":$accVar, "::mlir::ValueRange":$bounds,
|
|
"::mlir::ValueRange":$asyncOperands,
|
|
"::mlir::ArrayAttr":$asyncOperandsDeviceType,
|
|
"::mlir::ArrayAttr":$asyncOnly,
|
|
"::mlir::acc::DataClause":$dataClause, "bool":$structured,
|
|
"bool":$implicit, "::mlir::StringAttr":$name),
|
|
[{
|
|
build($_builder, $_state, accVar, bounds, asyncOperands,
|
|
asyncOperandsDeviceType, asyncOnly, dataClause, structured,
|
|
implicit, ::mlir::acc::DataClauseModifier::none, name);
|
|
}]>,
|
|
];
|
|
|
|
code extraClassDeclarationDataExit = [{
|
|
mlir::TypedValue<mlir::acc::PointerLikeType> getAccPtr() {
|
|
return mlir::dyn_cast<mlir::TypedValue<mlir::acc::PointerLikeType>>(getAccVar());
|
|
}
|
|
}];
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.7.8 copyout clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_CopyoutOp : OpenACC_DataExitOpWithVarPtr<"copyout",
|
|
"mlir::acc::DataClause::acc_copyout"> {
|
|
let summary = "Represents acc copyout semantics - reverse of copyin.";
|
|
|
|
let extraClassDeclaration = extraClassDeclarationBase # extraClassDeclarationDataExit # [{
|
|
/// Check if this is a copyout with zero modifier.
|
|
bool isCopyoutZero();
|
|
}];
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.7.11 delete clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_DeleteOp : OpenACC_DataExitOpNoVarPtr<"delete",
|
|
"mlir::acc::DataClause::acc_delete"> {
|
|
let summary = "Represents acc delete semantics - reverse of create.";
|
|
let extraClassDeclaration = extraClassDeclarationBase # extraClassDeclarationDataExit;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.7.13 detach clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_DetachOp : OpenACC_DataExitOpNoVarPtr<"detach",
|
|
"mlir::acc::DataClause::acc_detach"> {
|
|
let summary = "Represents acc detach semantics - reverse of attach.";
|
|
let extraClassDeclaration = extraClassDeclarationBase # extraClassDeclarationDataExit;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.14.4 host clause
|
|
//===----------------------------------------------------------------------===//
|
|
def OpenACC_UpdateHostOp : OpenACC_DataExitOpWithVarPtr<"update_host",
|
|
"mlir::acc::DataClause::acc_update_host"> {
|
|
let summary = "Represents acc update host semantics.";
|
|
let extraClassDeclaration = extraClassDeclarationBase # extraClassDeclarationDataExit # [{
|
|
/// Check if this is an acc update self.
|
|
bool isSelf() {
|
|
return getDataClause() == acc::DataClause::acc_update_self;
|
|
}
|
|
}];
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.5.13 private clause
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_PrivateRecipeOp
|
|
: OpenACC_Op<"private.recipe", [IsolatedFromAbove, Symbol, RecipeInterface,
|
|
AutomaticAllocationScope]> {
|
|
let summary = "privatization recipe";
|
|
|
|
let description = [{
|
|
Declares an OpenACC privatization recipe. The operation requires one
|
|
mandatory and one optional region.
|
|
|
|
1. The initializer region specifies how to allocate and initialize a new
|
|
private value. For example in Fortran, a derived-type might have a
|
|
default initialization. The region has an argument that contains the
|
|
original value that needs to be privatized, followed by bounds arguments
|
|
(if any) in order from innermost to outermost dimension. The region
|
|
must yield the privatized copy.
|
|
2. The destroy region specifies how to destruct the value when it reaches
|
|
its end of life. It takes the original value, the privatized value, and
|
|
bounds arguments (if any) in the same order as the init region.
|
|
|
|
A single privatization recipe can be used for multiple operand if they have
|
|
the same type and do not require a specific default initialization.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.private.recipe @privatization_memref : memref<10x20xf32> init {
|
|
^bb0(%original: memref<10x20xf32>):
|
|
// init region contains a sequence of operations to create and
|
|
// initialize the copy. It yields the privatized copy.
|
|
%alloca = memref.alloca() : memref<10x20xf32>
|
|
acc.yield %alloca : memref<10x20xf32>
|
|
} destroy {
|
|
^bb0(%original: memref<10x20xf32>, %privatized: memref<10x20xf32>):
|
|
// destroy region is empty since alloca is automatically cleaned up
|
|
acc.terminator
|
|
}
|
|
|
|
// Example with bounds for array slicing:
|
|
acc.private.recipe @privatization_slice : memref<10x20xf32> init {
|
|
^bb0(%original: memref<10x20xf32>, %bounds_inner: !acc.data_bounds_ty, %bounds_outer: !acc.data_bounds_ty):
|
|
// Extract bounds and create appropriately sized allocation
|
|
%extent_inner = acc.get_extent %bounds_inner : (!acc.data_bounds_ty) -> index
|
|
%extent_outer = acc.get_extent %bounds_outer : (!acc.data_bounds_ty) -> index
|
|
%slice_alloc = memref.alloca(%extent_outer, %extent_inner) : memref<?x?xf32>
|
|
// ... base pointer adjustment logic ...
|
|
acc.yield %result : memref<10x20xf32>
|
|
} destroy {
|
|
^bb0(%original: memref<10x20xf32>, %privatized: memref<10x20xf32>, %bounds_inner: !acc.data_bounds_ty, %bounds_outer: !acc.data_bounds_ty):
|
|
// Cleanup is automatic for alloca-based allocations
|
|
acc.terminator
|
|
}
|
|
|
|
// The privatization symbol is then used in the corresponding operation.
|
|
acc.parallel private(@privatization_memref -> %a : memref<10x20xf32>) {
|
|
}
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins SymbolNameAttr:$sym_name,
|
|
TypeAttr:$type);
|
|
|
|
let regions = (region AnyRegion:$initRegion,
|
|
AnyRegion:$destroyRegion);
|
|
|
|
let assemblyFormat = [{
|
|
$sym_name `:` $type attr-dict-with-keyword `init` $initRegion
|
|
(`destroy` $destroyRegion^)?
|
|
}];
|
|
|
|
let hasRegionVerifier = 1;
|
|
|
|
let extraClassDeclaration = [{
|
|
/// Creates a PrivateRecipeOp and populates its regions based on the
|
|
/// variable type as long as the type implements MappableType or
|
|
/// PointerLikeType interface. If a type implements both, the MappableType
|
|
/// API will be preferred. Returns std::nullopt if the recipe cannot be
|
|
/// created or populated. The builder's current insertion point will be used
|
|
/// and it must be a valid place for this operation to be inserted. The
|
|
/// `recipeName` must be a unique name to prevent "redefinition of symbol"
|
|
/// IR errors.
|
|
/// The `hostVar` is the original host variable from which the type and
|
|
/// language-specific metadata are derived.
|
|
static std::optional<PrivateRecipeOp> createAndPopulate(
|
|
::mlir::OpBuilder &builder,
|
|
::mlir::Location loc,
|
|
::llvm::StringRef recipeName,
|
|
::mlir::Value hostVar,
|
|
::llvm::StringRef varName = "",
|
|
::mlir::ValueRange bounds = {});
|
|
|
|
/// Creates a PrivateRecipeOp using the same variable type as an existing
|
|
/// FirstprivateRecipeOp. This is a useful in cases where we promote private variables to firstprivate by analysis
|
|
/// This function reuses the init region from a firstprivate recipe when building a private
|
|
/// recipe. Callers thus must ensure that this is semantically valid for the language
|
|
/// lowering (e.g. that private does not perform extra default initialization
|
|
/// that firstprivate intentionally omits, such as for C++ classes or Fortran
|
|
/// derived types with default initialization).
|
|
static std::optional<PrivateRecipeOp> createAndPopulate(
|
|
::mlir::OpBuilder &builder,
|
|
::mlir::Location loc,
|
|
::llvm::StringRef recipeName,
|
|
::mlir::acc::FirstprivateRecipeOp firstprivRecipe);
|
|
}];
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.5.14 firstprivate clause
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_FirstprivateRecipeOp
|
|
: OpenACC_Op<"firstprivate.recipe", [IsolatedFromAbove, Symbol,
|
|
RecipeInterface,
|
|
AutomaticAllocationScope]> {
|
|
let summary = "privatization recipe";
|
|
|
|
let description = [{
|
|
Declares an OpenACC privatization recipe with copy of the initial value.
|
|
The operation requires two mandatory regions and one optional.
|
|
|
|
1. The initializer region specifies how to allocate and initialize a new
|
|
private value. For example in Fortran, a derived-type might have a
|
|
default initialization. The region has an argument that contains the
|
|
original value that needs to be privatized, followed by bounds arguments
|
|
(if any) in order from innermost to outermost dimension. The region must
|
|
yield the privatized copy.
|
|
2. The copy region specifies how to copy the initial value to the newly
|
|
created private value. It takes the original value, the privatized
|
|
value, followed by bounds arguments (if any) in the same order.
|
|
3. The destroy region specifies how to destruct the value when it reaches
|
|
its end of life. It takes the original value, the privatized value, and
|
|
bounds arguments (if any) in the same order. It is optional.
|
|
|
|
A single privatization recipe can be used for multiple operand if they have
|
|
the same type and do not require a specific default initialization.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.firstprivate.recipe @firstprivate_memref : memref<10x20xf32> init {
|
|
^bb0(%original: memref<10x20xf32>):
|
|
// init region contains a sequence of operations to create and
|
|
// initialize the copy. It yields the privatized copy.
|
|
%alloca = memref.alloca() : memref<10x20xf32>
|
|
acc.yield %alloca : memref<10x20xf32>
|
|
} copy {
|
|
^bb0(%original: memref<10x20xf32>, %privatized: memref<10x20xf32>):
|
|
// copy region contains a sequence of operations to copy the initial value
|
|
// of the firstprivate value to the newly created value.
|
|
memref.copy %original, %privatized : memref<10x20xf32> to memref<10x20xf32>
|
|
acc.terminator
|
|
} destroy {
|
|
^bb0(%original: memref<10x20xf32>, %privatized: memref<10x20xf32>):
|
|
// destroy region is empty since alloca is automatically cleaned up
|
|
acc.terminator
|
|
}
|
|
|
|
// Example with bounds for array slicing:
|
|
acc.firstprivate.recipe @firstprivate_slice : memref<10x20xf32> init {
|
|
^bb0(%original: memref<10x20xf32>, %bounds_inner: !acc.data_bounds_ty, %bounds_outer: !acc.data_bounds_ty):
|
|
// Extract bounds and create appropriately sized allocation
|
|
%extent_inner = acc.get_extent %bounds_inner : (!acc.data_bounds_ty) -> index
|
|
%extent_outer = acc.get_extent %bounds_outer : (!acc.data_bounds_ty) -> index
|
|
%slice_alloc = memref.alloca(%extent_outer, %extent_inner) : memref<?x?xf32>
|
|
// ... base pointer adjustment logic ...
|
|
acc.yield %result : memref<10x20xf32>
|
|
} copy {
|
|
^bb0(%original: memref<10x20xf32>, %privatized: memref<10x20xf32>, %bounds_inner: !acc.data_bounds_ty, %bounds_outer: !acc.data_bounds_ty):
|
|
// Copy the slice portion from original to privatized
|
|
%lb_inner = acc.get_lowerbound %bounds_inner : (!acc.data_bounds_ty) -> index
|
|
%lb_outer = acc.get_lowerbound %bounds_outer : (!acc.data_bounds_ty) -> index
|
|
%extent_inner = acc.get_extent %bounds_inner : (!acc.data_bounds_ty) -> index
|
|
%extent_outer = acc.get_extent %bounds_outer : (!acc.data_bounds_ty) -> index
|
|
%subview = memref.subview %original[%lb_outer, %lb_inner][%extent_outer, %extent_inner][1, 1]
|
|
: memref<10x20xf32> to memref<?x?xf32, strided<[20, 1], offset: ?>>
|
|
// Copy subview to privatized...
|
|
acc.terminator
|
|
}
|
|
|
|
// The privatization symbol is then used in the corresponding operation.
|
|
acc.parallel firstprivate(@firstprivate_memref -> %a : memref<10x20xf32>) {
|
|
}
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins SymbolNameAttr:$sym_name,
|
|
TypeAttr:$type);
|
|
|
|
let regions = (region AnyRegion:$initRegion, AnyRegion:$copyRegion,
|
|
AnyRegion:$destroyRegion);
|
|
|
|
let assemblyFormat = [{
|
|
$sym_name `:` $type attr-dict-with-keyword `init` $initRegion
|
|
`copy` $copyRegion
|
|
(`destroy` $destroyRegion^)?
|
|
}];
|
|
|
|
let hasRegionVerifier = 1;
|
|
|
|
let extraClassDeclaration = [{
|
|
/// Creates a FirstprivateRecipeOp and populates its regions based on the
|
|
/// variable type as long as the type implements MappableType or
|
|
/// PointerLikeType interface. If a type implements both, the MappableType
|
|
/// API will be preferred. Returns std::nullopt if the recipe cannot be
|
|
/// created or populated. The builder's current insertion point will be used
|
|
/// and it must be a valid place for this operation to be inserted. The
|
|
/// `recipeName` must be a unique name to prevent "redefinition of symbol"
|
|
/// IR errors.
|
|
/// The `hostVar` is the original host variable from which the type and
|
|
/// language-specific metadata are derived.
|
|
static std::optional<FirstprivateRecipeOp> createAndPopulate(
|
|
::mlir::OpBuilder &builder,
|
|
::mlir::Location loc,
|
|
::llvm::StringRef recipeName,
|
|
::mlir::Value hostVar,
|
|
::llvm::StringRef varName = "",
|
|
::mlir::ValueRange bounds = {});
|
|
}];
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.5.15 reduction clause
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_ReductionRecipeOp
|
|
: OpenACC_Op<"reduction.recipe", [IsolatedFromAbove, Symbol,
|
|
RecipeInterface,
|
|
AutomaticAllocationScope]> {
|
|
let summary = "reduction recipe";
|
|
|
|
let description = [{
|
|
Declares an OpenACC reduction recipe. The operation requires two
|
|
mandatory regions and one optional region.
|
|
|
|
1. The initializer region specifies how to initialize the local reduction
|
|
value. The region has a first argument that contains the original value
|
|
that needs to be reduced, followed by bounds arguments (if any) in order
|
|
from innermost to outermost dimension. It is expected to `acc.yield` the
|
|
initialized reduction value.
|
|
2. The combiner region contains a sequence of operations to combine two
|
|
values of the reduction type into one. It has the first reduction value,
|
|
the second reduction value, followed by bounds arguments (if any) in the
|
|
same order. It is expected to `acc.yield` the combined value.
|
|
3. The optional destroy region specifies how to destruct the value when it
|
|
reaches its end of life. It takes the original value, the reduction value,
|
|
and bounds arguments (if any) in the same order.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.reduction.recipe @reduction_add_memref : memref<10x20xf32> reduction_operator<add> init {
|
|
^bb0(%original: memref<10x20xf32>):
|
|
// init region contains a sequence of operations to initialize the local
|
|
// reduction value as specified in 2.5.15
|
|
%alloca = memref.alloca() : memref<10x20xf32>
|
|
%cst = arith.constant 0.0 : f32
|
|
linalg.fill ins(%cst : f32) outs(%alloca : memref<10x20xf32>)
|
|
acc.yield %alloca : memref<10x20xf32>
|
|
} combiner {
|
|
^bb0(%lhs: memref<10x20xf32>, %rhs: memref<10x20xf32>):
|
|
// combiner region contains a sequence of operations to combine
|
|
// two values into one.
|
|
linalg.add ins(%lhs, %rhs : memref<10x20xf32>, memref<10x20xf32>)
|
|
outs(%lhs : memref<10x20xf32>)
|
|
acc.yield %lhs : memref<10x20xf32>
|
|
} destroy {
|
|
^bb0(%original: memref<10x20xf32>, %reduction: memref<10x20xf32>):
|
|
// destroy region is empty since alloca is automatically cleaned up
|
|
acc.terminator
|
|
}
|
|
|
|
// Example with bounds for array slicing:
|
|
acc.reduction.recipe @reduction_add_slice : memref<10x20xf32> reduction_operator<add> init {
|
|
^bb0(%original: memref<10x20xf32>, %bounds_inner: !acc.data_bounds_ty, %bounds_outer: !acc.data_bounds_ty):
|
|
// Extract bounds and create appropriately sized allocation
|
|
%extent_inner = acc.get_extent %bounds_inner : (!acc.data_bounds_ty) -> index
|
|
%extent_outer = acc.get_extent %bounds_outer : (!acc.data_bounds_ty) -> index
|
|
%slice_alloc = memref.alloca(%extent_outer, %extent_inner) : memref<?x?xf32>
|
|
%cst = arith.constant 0.0 : f32
|
|
linalg.fill ins(%cst : f32) outs(%slice_alloc : memref<?x?xf32>)
|
|
// ... base pointer adjustment logic ...
|
|
acc.yield %result : memref<10x20xf32>
|
|
} combiner {
|
|
^bb0(%lhs: memref<10x20xf32>, %rhs: memref<10x20xf32>, %bounds_inner: !acc.data_bounds_ty, %bounds_outer: !acc.data_bounds_ty):
|
|
// Extract bounds to operate only on the slice portion
|
|
%lb_inner = acc.get_lowerbound %bounds_inner : (!acc.data_bounds_ty) -> index
|
|
%lb_outer = acc.get_lowerbound %bounds_outer : (!acc.data_bounds_ty) -> index
|
|
%extent_inner = acc.get_extent %bounds_inner : (!acc.data_bounds_ty) -> index
|
|
%extent_outer = acc.get_extent %bounds_outer : (!acc.data_bounds_ty) -> index
|
|
|
|
// Create subviews to access only the slice portions
|
|
%lhs_slice = memref.subview %lhs[%lb_outer, %lb_inner][%extent_outer, %extent_inner][1, 1]
|
|
: memref<10x20xf32> to memref<?x?xf32, strided<[20, 1], offset: ?>>
|
|
%rhs_slice = memref.subview %rhs[%lb_outer, %lb_inner][%extent_outer, %extent_inner][1, 1]
|
|
: memref<10x20xf32> to memref<?x?xf32, strided<[20, 1], offset: ?>>
|
|
|
|
// Combine only the slice portions
|
|
linalg.add ins(%lhs_slice, %rhs_slice : memref<?x?xf32, strided<[20, 1], offset: ?>>, memref<?x?xf32, strided<[20, 1], offset: ?>>)
|
|
outs(%lhs_slice : memref<?x?xf32, strided<[20, 1], offset: ?>>)
|
|
acc.yield %lhs : memref<10x20xf32>
|
|
}
|
|
|
|
// The reduction symbol is then used in the corresponding operation.
|
|
acc.parallel reduction(@reduction_add_memref -> %a : memref<10x20xf32>) {
|
|
}
|
|
```
|
|
|
|
The following table lists the valid operators and the initialization values
|
|
according to OpenACC 3.3:
|
|
|
|
|------------------------------------------------|
|
|
| C/C++ | Fortran |
|
|
|-----------------------|------------------------|
|
|
| operator | init value | operator | init value |
|
|
| + | 0 | + | 0 |
|
|
| * | 1 | * | 1 |
|
|
| max | least | max | least |
|
|
| min | largest | min | largest |
|
|
| & | ~0 | iand | all bits on |
|
|
| | | 0 | ior | 0 |
|
|
| ^ | 0 | ieor | 0 |
|
|
| && | 1 | .and. | .true. |
|
|
| || | 0 | .or. | .false. |
|
|
| | | .eqv. | .true. |
|
|
| | | .neqv. | .false. |
|
|
-------------------------------------------------|
|
|
}];
|
|
|
|
let arguments = (ins SymbolNameAttr:$sym_name,
|
|
TypeAttr:$type,
|
|
OpenACC_ReductionOperatorAttr:$reductionOperator);
|
|
|
|
let regions = (region AnyRegion:$initRegion,
|
|
AnyRegion:$combinerRegion,
|
|
AnyRegion:$destroyRegion);
|
|
|
|
let assemblyFormat = [{
|
|
$sym_name `:` $type attr-dict-with-keyword
|
|
`reduction_operator` $reductionOperator
|
|
`init` $initRegion `combiner` $combinerRegion
|
|
(`destroy` $destroyRegion^)?
|
|
}];
|
|
|
|
// TODO: we need to verify that reduction operators
|
|
// maxnumf, maximumf, minnumf anf minimumf are only applied
|
|
// to FloatType element types.
|
|
let hasRegionVerifier = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.5.1 parallel Construct
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_ParallelOp
|
|
: OpenACC_Op<"parallel",
|
|
[AttrSizedOperandSegments, AutomaticAllocationScope,
|
|
RecursiveMemoryEffects,
|
|
DeclareOpInterfaceMethods<ComputeRegionOpInterface>,
|
|
DeclareOpInterfaceMethods<RegionBranchOpInterface,
|
|
["getSuccessorInputs"]>,
|
|
OffloadRegionOpInterface,
|
|
MemoryEffects<[MemWrite<OpenACC_ConstructResource>,
|
|
MemRead<OpenACC_CurrentDeviceIdResource>]>]> {
|
|
let summary = "parallel construct";
|
|
let description = [{
|
|
The "acc.parallel" operation represents a parallel construct block. It has
|
|
one region to be executed in parallel on the current device.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.parallel num_gangs(%c10) num_workers(%c10)
|
|
private(%c : memref<10xf32>) {
|
|
// parallel region
|
|
}
|
|
```
|
|
|
|
`async`, `wait`, `num_gangs`, `num_workers` and `vector_length` operands are
|
|
supported with `device_type` information. They should only be accessed by
|
|
the extra provided getters. If modified, the corresponding `device_type`
|
|
attributes must be modified as well.
|
|
}];
|
|
|
|
let arguments = (ins
|
|
Variadic<IntOrIndex>:$asyncOperands,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOperandsDeviceType,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOnly,
|
|
Variadic<IntOrIndex>:$waitOperands,
|
|
OptionalAttr<DenseI32ArrayAttr>:$waitOperandsSegments,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$waitOperandsDeviceType,
|
|
OptionalAttr<BoolArrayAttr>:$hasWaitDevnum,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$waitOnly,
|
|
Variadic<IntOrIndex>:$numGangs,
|
|
OptionalAttr<DenseI32ArrayAttr>:$numGangsSegments,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$numGangsDeviceType,
|
|
Variadic<IntOrIndex>:$numWorkers,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$numWorkersDeviceType,
|
|
Variadic<IntOrIndex>:$vectorLength,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$vectorLengthDeviceType,
|
|
Optional<I1>:$ifCond,
|
|
Optional<I1>:$selfCond,
|
|
UnitAttr:$selfAttr,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$reductionOperands,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$privateOperands,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$firstprivateOperands,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$dataClauseOperands,
|
|
OptionalAttr<DefaultValueAttr>:$defaultAttr,
|
|
UnitAttr:$combined);
|
|
|
|
let regions = (region AnyRegion:$region);
|
|
|
|
let builders = [
|
|
OpBuilder<(ins
|
|
CArg<"mlir::ValueRange", "{}">:$numGangs,
|
|
CArg<"mlir::ValueRange", "{}">:$numWorkers,
|
|
CArg<"mlir::ValueRange", "{}">:$vectorLength,
|
|
CArg<"mlir::ValueRange", "{}">:$asyncOperands,
|
|
CArg<"mlir::ValueRange", "{}">:$waitOperands,
|
|
CArg<"mlir::Value", "{}">:$ifCond,
|
|
CArg<"mlir::Value", "{}">:$selfCond,
|
|
CArg<"mlir::ValueRange", "{}">:$reductionOperands,
|
|
CArg<"mlir::ValueRange", "{}">:$privateOperands,
|
|
CArg<"mlir::ValueRange", "{}">:$firstprivateOperands,
|
|
CArg<"mlir::ValueRange", "{}">:$dataClauseOperands)>];
|
|
|
|
let extraClassDeclaration = [{
|
|
/// The number of data operands.
|
|
unsigned getNumDataOperands();
|
|
|
|
/// The i-th data operand passed.
|
|
Value getDataOperand(unsigned i);
|
|
|
|
/// Used to retrieve the block inside the op's region.
|
|
Block &getBody() { return getRegion().front(); }
|
|
|
|
/// Return true if the op has the async attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasAsyncOnly();
|
|
/// Return true if the op has the async attribute for the given device_type.
|
|
bool hasAsyncOnly(mlir::acc::DeviceType deviceType);
|
|
/// Return the value of the async clause if present.
|
|
mlir::Value getAsyncValue();
|
|
/// Return the value of the async clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getAsyncValue(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return the value of the num_workers clause if present.
|
|
mlir::Value getNumWorkersValue();
|
|
/// Return the value of the num_workers clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getNumWorkersValue(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return the value of the vector_length clause if present.
|
|
mlir::Value getVectorLengthValue();
|
|
/// Return the value of the vector_length clause for the given device_type
|
|
/// if present.
|
|
mlir::Value getVectorLengthValue(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return the values of the num_gangs clause if present.
|
|
mlir::Operation::operand_range getNumGangsValues();
|
|
/// Return the values of the num_gangs clause for the given device_type if
|
|
/// present.
|
|
mlir::Operation::operand_range
|
|
getNumGangsValues(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return true if the op has any num_gangs, num_workers, or vector_length
|
|
/// clause for the given device_type.
|
|
bool hasAnyGangWorkerVector(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return true if the op has the wait attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasWaitOnly();
|
|
/// Return true if the op has the wait attribute for the given device_type.
|
|
bool hasWaitOnly(mlir::acc::DeviceType deviceType);
|
|
/// Return the values of the wait clause if present.
|
|
mlir::Operation::operand_range getWaitValues();
|
|
/// Return the values of the wait clause for the given device_type if
|
|
/// present.
|
|
mlir::Operation::operand_range
|
|
getWaitValues(mlir::acc::DeviceType deviceType);
|
|
/// Return the wait devnum value clause if present;
|
|
mlir::Value getWaitDevnum();
|
|
/// Return the wait devnum value clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getWaitDevnum(mlir::acc::DeviceType deviceType);
|
|
static mlir::acc::Construct getConstructId() {
|
|
return mlir::acc::Construct::acc_construct_parallel;
|
|
}
|
|
/// Add a value to 'num_workers' with the current list of device types.
|
|
void addNumWorkersOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add a value to 'vector_length' with the current list of device types.
|
|
void addVectorLengthOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add an entry to the 'async-only' attribute (clause spelled without
|
|
/// arguments)for each of the additional device types (or a none if it is
|
|
/// empty).
|
|
void addAsyncOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add a value to the 'async' with the current list of device types.
|
|
void addAsyncOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add an array-like entry to the 'num_gangs' with the current list of
|
|
/// device types.
|
|
void addNumGangsOperands(MLIRContext *, mlir::ValueRange,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add an entry to the 'wait-only' attribute (clause spelled without
|
|
/// arguments)for each of the additional device types (or a none if it is
|
|
/// empty).
|
|
void addWaitOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add an array-like entry to the 'wait' with the current list of device
|
|
/// types.
|
|
void addWaitOperands(MLIRContext *, bool hasDevnum, mlir::ValueRange,
|
|
llvm::ArrayRef<DeviceType>);
|
|
|
|
/// Adds a private clause variable to this operation, including its recipe.
|
|
void addPrivatization(MLIRContext *, mlir::acc::PrivateOp op,
|
|
mlir::acc::PrivateRecipeOp recipe);
|
|
/// Adds a firstprivate clause variable to this operation, including its
|
|
/// recipe.
|
|
void addFirstPrivatization(MLIRContext *, mlir::acc::FirstprivateOp op,
|
|
mlir::acc::FirstprivateRecipeOp recipe);
|
|
|
|
/// Adds a reduction clause variable to this operation, including its
|
|
/// recipe.
|
|
void addReduction(MLIRContext *, mlir::acc::ReductionOp op,
|
|
mlir::acc::ReductionRecipeOp recipe);
|
|
}];
|
|
|
|
let assemblyFormat = [{
|
|
( `combined` `(` `loop` `)` $combined^)?
|
|
oilist(
|
|
`dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
|
|
| `async` `` custom<DeviceTypeOperandsWithKeywordOnly>($asyncOperands,
|
|
type($asyncOperands), $asyncOperandsDeviceType, $asyncOnly)
|
|
| `firstprivate` `(` $firstprivateOperands `:` type($firstprivateOperands) `)`
|
|
| `num_gangs` `(` custom<NumGangs>($numGangs,
|
|
type($numGangs), $numGangsDeviceType, $numGangsSegments) `)`
|
|
| `num_workers` `(` custom<DeviceTypeOperands>($numWorkers,
|
|
type($numWorkers), $numWorkersDeviceType) `)`
|
|
| `private` `(` $privateOperands `:` type($privateOperands) `)`
|
|
| `vector_length` `(` custom<DeviceTypeOperands>($vectorLength,
|
|
type($vectorLength), $vectorLengthDeviceType) `)`
|
|
| `wait` `` custom<WaitClause>($waitOperands, type($waitOperands),
|
|
$waitOperandsDeviceType, $waitOperandsSegments, $hasWaitDevnum,
|
|
$waitOnly)
|
|
| `self` `(` $selfCond `)`
|
|
| `if` `(` $ifCond `)`
|
|
| `reduction` `(` $reductionOperands `:` type($reductionOperands) `)`
|
|
)
|
|
$region attr-dict-with-keyword
|
|
}];
|
|
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.5.2 serial Construct
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_SerialOp
|
|
: OpenACC_Op<"serial",
|
|
[AttrSizedOperandSegments, AutomaticAllocationScope,
|
|
RecursiveMemoryEffects,
|
|
DeclareOpInterfaceMethods<ComputeRegionOpInterface>,
|
|
DeclareOpInterfaceMethods<RegionBranchOpInterface,
|
|
["getSuccessorInputs"]>,
|
|
OffloadRegionOpInterface,
|
|
MemoryEffects<[MemWrite<OpenACC_ConstructResource>,
|
|
MemRead<OpenACC_CurrentDeviceIdResource>]>]> {
|
|
let summary = "serial construct";
|
|
let description = [{
|
|
The "acc.serial" operation represents a serial construct block. It has
|
|
one region to be executed in serial on the current device.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.serial private(%c : memref<10xf32>) {
|
|
// serial region
|
|
}
|
|
```
|
|
|
|
`async` and `wait` operands are supported with `device_type` information.
|
|
They should only be accessed by the extra provided getters. If modified,
|
|
the corresponding `device_type` attributes must be modified as well.
|
|
}];
|
|
|
|
let arguments = (ins
|
|
Variadic<IntOrIndex>:$asyncOperands,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOperandsDeviceType,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOnly,
|
|
Variadic<IntOrIndex>:$waitOperands,
|
|
OptionalAttr<DenseI32ArrayAttr>:$waitOperandsSegments,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$waitOperandsDeviceType,
|
|
OptionalAttr<BoolArrayAttr>:$hasWaitDevnum,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$waitOnly,
|
|
Optional<I1>:$ifCond,
|
|
Optional<I1>:$selfCond,
|
|
UnitAttr:$selfAttr,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$reductionOperands,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$privateOperands,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$firstprivateOperands,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$dataClauseOperands,
|
|
OptionalAttr<DefaultValueAttr>:$defaultAttr,
|
|
UnitAttr:$combined);
|
|
|
|
let regions = (region AnyRegion:$region);
|
|
|
|
let extraClassDeclaration = [{
|
|
/// The number of data operands.
|
|
unsigned getNumDataOperands();
|
|
|
|
/// The i-th data operand passed.
|
|
Value getDataOperand(unsigned i);
|
|
|
|
/// Used to retrieve the block inside the op's region.
|
|
Block &getBody() { return getRegion().front(); }
|
|
|
|
/// Return true if the op has the async attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasAsyncOnly();
|
|
/// Return true if the op has the async attribute for the given device_type.
|
|
bool hasAsyncOnly(mlir::acc::DeviceType deviceType);
|
|
/// Return the value of the async clause if present.
|
|
mlir::Value getAsyncValue();
|
|
/// Return the value of the async clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getAsyncValue(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return true if the op has the wait attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasWaitOnly();
|
|
/// Return true if the op has the wait attribute for the given device_type.
|
|
bool hasWaitOnly(mlir::acc::DeviceType deviceType);
|
|
/// Return the values of the wait clause if present.
|
|
mlir::Operation::operand_range getWaitValues();
|
|
/// Return the values of the wait clause for the given device_type if
|
|
/// present.
|
|
mlir::Operation::operand_range
|
|
getWaitValues(mlir::acc::DeviceType deviceType);
|
|
/// Return the wait devnum value clause if present;
|
|
mlir::Value getWaitDevnum();
|
|
/// Return the wait devnum value clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getWaitDevnum(mlir::acc::DeviceType deviceType);
|
|
static mlir::acc::Construct getConstructId() {
|
|
return mlir::acc::Construct::acc_construct_serial;
|
|
}
|
|
/// Add an entry to the 'async-only' attribute (clause spelled without
|
|
/// arguments) for each of the additional device types (or a none if it is
|
|
/// empty).
|
|
void addAsyncOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add a value to the 'async' with the current list of device types.
|
|
void addAsyncOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add an entry to the 'wait-only' attribute (clause spelled without
|
|
/// arguments) for each of the additional device types (or a none if it is
|
|
/// empty).
|
|
void addWaitOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add an array-like entry to the 'wait' with the current list of device
|
|
/// types.
|
|
void addWaitOperands(MLIRContext *, bool hasDevnum, mlir::ValueRange,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Adds a private clause variable to this operation, including its recipe.
|
|
void addPrivatization(MLIRContext *, mlir::acc::PrivateOp op,
|
|
mlir::acc::PrivateRecipeOp recipe);
|
|
/// Adds a firstprivate clause variable to this operation, including its
|
|
/// recipe.
|
|
void addFirstPrivatization(MLIRContext *, mlir::acc::FirstprivateOp op,
|
|
mlir::acc::FirstprivateRecipeOp recipe);
|
|
/// Adds a reduction clause variable to this operation, including its
|
|
/// recipe.
|
|
void addReduction(MLIRContext *, mlir::acc::ReductionOp op,
|
|
mlir::acc::ReductionRecipeOp recipe);
|
|
}];
|
|
|
|
let assemblyFormat = [{
|
|
( `combined` `(` `loop` `)` $combined^)?
|
|
oilist(
|
|
`dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
|
|
| `async` `` custom<DeviceTypeOperandsWithKeywordOnly>($asyncOperands,
|
|
type($asyncOperands), $asyncOperandsDeviceType, $asyncOnly)
|
|
| `firstprivate` `(` $firstprivateOperands `:` type($firstprivateOperands) `)`
|
|
| `private` `(` $privateOperands `:` type($privateOperands) `)`
|
|
| `wait` `` custom<WaitClause>($waitOperands, type($waitOperands),
|
|
$waitOperandsDeviceType, $waitOperandsSegments, $hasWaitDevnum,
|
|
$waitOnly)
|
|
| `self` `(` $selfCond `)`
|
|
| `if` `(` $ifCond `)`
|
|
| `reduction` `(` $reductionOperands `:` type($reductionOperands) `)`
|
|
)
|
|
$region attr-dict-with-keyword
|
|
}];
|
|
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.5.1 kernels Construct
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_KernelsOp
|
|
: OpenACC_Op<"kernels",
|
|
[AttrSizedOperandSegments, AutomaticAllocationScope,
|
|
RecursiveMemoryEffects,
|
|
DeclareOpInterfaceMethods<ComputeRegionOpInterface>,
|
|
DeclareOpInterfaceMethods<RegionBranchOpInterface,
|
|
["getSuccessorInputs"]>,
|
|
OffloadRegionOpInterface,
|
|
MemoryEffects<[MemWrite<OpenACC_ConstructResource>,
|
|
MemRead<OpenACC_CurrentDeviceIdResource>]>]> {
|
|
let summary = "kernels construct";
|
|
let description = [{
|
|
The "acc.kernels" operation represents a kernels construct block. It has
|
|
one region to be compiled into a sequence of kernels for execution on the
|
|
current device.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.kernels num_gangs(%c10) num_workers(%c10)
|
|
private(%c : memref<10xf32>) {
|
|
// kernels region
|
|
}
|
|
```
|
|
|
|
`collapse`, `gang`, `worker`, `vector`, `seq`, `independent`, `auto` and
|
|
`tile` operands are supported with `device_type` information. They should
|
|
only be accessed by the extra provided getters. If modified, the
|
|
corresponding `device_type` attributes must be modified as well.
|
|
}];
|
|
|
|
let arguments = (ins Variadic<IntOrIndex>:$asyncOperands,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOperandsDeviceType,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOnly,
|
|
Variadic<IntOrIndex>:$waitOperands,
|
|
OptionalAttr<DenseI32ArrayAttr>:$waitOperandsSegments,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$waitOperandsDeviceType,
|
|
OptionalAttr<BoolArrayAttr>:$hasWaitDevnum,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$waitOnly,
|
|
Variadic<IntOrIndex>:$numGangs,
|
|
OptionalAttr<DenseI32ArrayAttr>:$numGangsSegments,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$numGangsDeviceType,
|
|
Variadic<IntOrIndex>:$numWorkers,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$numWorkersDeviceType,
|
|
Variadic<IntOrIndex>:$vectorLength,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$vectorLengthDeviceType,
|
|
Optional<I1>:$ifCond, Optional<I1>:$selfCond, UnitAttr:$selfAttr,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$reductionOperands,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$privateOperands,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$firstprivateOperands,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$dataClauseOperands,
|
|
OptionalAttr<DefaultValueAttr>:$defaultAttr, UnitAttr:$combined);
|
|
|
|
let regions = (region AnyRegion:$region);
|
|
|
|
let extraClassDeclaration = [{
|
|
/// The number of data operands.
|
|
unsigned getNumDataOperands();
|
|
|
|
/// The i-th data operand passed.
|
|
Value getDataOperand(unsigned i);
|
|
|
|
/// Used to retrieve the block inside the op's region.
|
|
Block &getBody() { return getRegion().front(); }
|
|
|
|
/// Return true if the op has the async attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasAsyncOnly();
|
|
/// Return true if the op has the async attribute for the given device_type.
|
|
bool hasAsyncOnly(mlir::acc::DeviceType deviceType);
|
|
/// Return the value of the async clause if present.
|
|
mlir::Value getAsyncValue();
|
|
/// Return the value of the async clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getAsyncValue(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return the value of the num_workers clause if present.
|
|
mlir::Value getNumWorkersValue();
|
|
/// Return the value of the num_workers clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getNumWorkersValue(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return the value of the vector_length clause if present.
|
|
mlir::Value getVectorLengthValue();
|
|
/// Return the value of the vector_length clause for the given device_type
|
|
/// if present.
|
|
mlir::Value getVectorLengthValue(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return the values of the num_gangs clause if present.
|
|
mlir::Operation::operand_range getNumGangsValues();
|
|
/// Return the values of the num_gangs clause for the given device_type if
|
|
/// present.
|
|
mlir::Operation::operand_range
|
|
getNumGangsValues(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return true if the op has any num_gangs, num_workers, or vector_length
|
|
/// clause for the given device_type.
|
|
bool hasAnyGangWorkerVector(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return true if the op has the wait attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasWaitOnly();
|
|
/// Return true if the op has the wait attribute for the given device_type.
|
|
bool hasWaitOnly(mlir::acc::DeviceType deviceType);
|
|
/// Return the values of the wait clause if present.
|
|
mlir::Operation::operand_range getWaitValues();
|
|
/// Return the values of the wait clause for the given device_type if
|
|
/// present.
|
|
mlir::Operation::operand_range
|
|
getWaitValues(mlir::acc::DeviceType deviceType);
|
|
/// Return the wait devnum value clause if present;
|
|
mlir::Value getWaitDevnum();
|
|
/// Return the wait devnum value clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getWaitDevnum(mlir::acc::DeviceType deviceType);
|
|
static mlir::acc::Construct getConstructId() {
|
|
return mlir::acc::Construct::acc_construct_kernels;
|
|
}
|
|
/// Add a value to 'num_workers' with the current list of device types.
|
|
void addNumWorkersOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add a value to 'vector_length' with the current list of device types.
|
|
void addVectorLengthOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add an entry to the 'async-only' attribute (clause spelled without
|
|
/// arguments) for each of the additional device types (or a none if it is
|
|
/// empty).
|
|
void addAsyncOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add a value to the 'async' with the current list of device types.
|
|
void addAsyncOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add an array-like entry to the 'num_gangs' with the current list of
|
|
/// device types.
|
|
void addNumGangsOperands(MLIRContext *, mlir::ValueRange,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add an entry to the 'wait-only' attribute (clause spelled without
|
|
/// arguments) for each of the additional device types (or a none if it is
|
|
/// empty).
|
|
void addWaitOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add an array-like entry to the 'wait' with the current list of device
|
|
/// types.
|
|
void addWaitOperands(MLIRContext *, bool hasDevnum, mlir::ValueRange,
|
|
llvm::ArrayRef<DeviceType>);
|
|
|
|
/// Adds a private clause variable to this operation, including its recipe.
|
|
void addPrivatization(MLIRContext *, mlir::acc::PrivateOp op,
|
|
mlir::acc::PrivateRecipeOp recipe);
|
|
/// Adds a firstprivate clause variable to this operation, including its
|
|
/// recipe.
|
|
void addFirstPrivatization(MLIRContext *, mlir::acc::FirstprivateOp op,
|
|
mlir::acc::FirstprivateRecipeOp recipe);
|
|
/// Adds a reduction clause variable to this operation, including its
|
|
/// recipe.
|
|
void addReduction(MLIRContext *, mlir::acc::ReductionOp op,
|
|
mlir::acc::ReductionRecipeOp recipe);
|
|
}];
|
|
|
|
let assemblyFormat = [{
|
|
( `combined` `(` `loop` `)` $combined^)?
|
|
oilist(
|
|
`dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
|
|
| `async` `` custom<DeviceTypeOperandsWithKeywordOnly>($asyncOperands,
|
|
type($asyncOperands), $asyncOperandsDeviceType, $asyncOnly)
|
|
| `firstprivate` `(` $firstprivateOperands `:` type($firstprivateOperands) `)`
|
|
| `num_gangs` `(` custom<NumGangs>($numGangs,
|
|
type($numGangs), $numGangsDeviceType, $numGangsSegments) `)`
|
|
| `num_workers` `(` custom<DeviceTypeOperands>($numWorkers,
|
|
type($numWorkers), $numWorkersDeviceType) `)`
|
|
| `private` `(` $privateOperands `:` type($privateOperands) `)`
|
|
| `vector_length` `(` custom<DeviceTypeOperands>($vectorLength,
|
|
type($vectorLength), $vectorLengthDeviceType) `)`
|
|
| `wait` `` custom<WaitClause>($waitOperands, type($waitOperands),
|
|
$waitOperandsDeviceType, $waitOperandsSegments, $hasWaitDevnum,
|
|
$waitOnly)
|
|
| `self` `(` $selfCond `)`
|
|
| `if` `(` $ifCond `)`
|
|
| `reduction` `(` $reductionOperands `:` type($reductionOperands) `)`
|
|
)
|
|
$region attr-dict-with-keyword
|
|
}];
|
|
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.6.5 data Construct
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_DataOp
|
|
: OpenACC_Op<
|
|
"data", [AttrSizedOperandSegments, RecursiveMemoryEffects,
|
|
DeclareOpInterfaceMethods<RegionBranchOpInterface,
|
|
["getSuccessorInputs"]>,
|
|
MemoryEffects<[MemWrite<OpenACC_ConstructResource>,
|
|
MemRead<OpenACC_CurrentDeviceIdResource>]>]> {
|
|
let summary = "data construct";
|
|
|
|
let description = [{
|
|
The "acc.data" operation represents a data construct. It defines vars to
|
|
be allocated in the current device memory for the duration of the region,
|
|
whether data should be copied from local memory to the current device
|
|
memory upon region entry , and copied from device memory to local memory
|
|
upon region exit.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.data present(%a: memref<10x10xf32>, %b: memref<10x10xf32>,
|
|
%c: memref<10xf32>, %d: memref<10xf32>) {
|
|
// data region
|
|
}
|
|
```
|
|
|
|
`async` and `wait` operands are supported with `device_type` information.
|
|
They should only be accessed by the extra provided getters. If modified,
|
|
the corresponding `device_type` attributes must be modified as well.
|
|
}];
|
|
|
|
|
|
let arguments = (ins Optional<I1>:$ifCond,
|
|
Variadic<IntOrIndex>:$asyncOperands,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOperandsDeviceType,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOnly,
|
|
Variadic<IntOrIndex>:$waitOperands,
|
|
OptionalAttr<DenseI32ArrayAttr>:$waitOperandsSegments,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$waitOperandsDeviceType,
|
|
OptionalAttr<BoolArrayAttr>:$hasWaitDevnum,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$waitOnly,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$dataClauseOperands,
|
|
OptionalAttr<DefaultValueAttr>:$defaultAttr);
|
|
|
|
let regions = (region AnyRegion:$region);
|
|
|
|
let extraClassDeclaration = [{
|
|
/// The number of data operands.
|
|
unsigned getNumDataOperands();
|
|
|
|
/// The i-th data operand passed.
|
|
Value getDataOperand(unsigned i);
|
|
|
|
/// Return true if the op has the async attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasAsyncOnly();
|
|
/// Return true if the op has the async attribute for the given device_type.
|
|
bool hasAsyncOnly(mlir::acc::DeviceType deviceType);
|
|
/// Return the value of the async clause if present.
|
|
mlir::Value getAsyncValue();
|
|
/// Return the value of the async clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getAsyncValue(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return true if the op has the wait attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasWaitOnly();
|
|
/// Return true if the op has the wait attribute for the given device_type.
|
|
bool hasWaitOnly(mlir::acc::DeviceType deviceType);
|
|
/// Return the values of the wait clause if present.
|
|
mlir::Operation::operand_range getWaitValues();
|
|
/// Return the values of the wait clause for the given device_type if
|
|
/// present.
|
|
mlir::Operation::operand_range
|
|
getWaitValues(mlir::acc::DeviceType deviceType);
|
|
/// Return the wait devnum value clause if present;
|
|
mlir::Value getWaitDevnum();
|
|
/// Return the wait devnum value clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getWaitDevnum(mlir::acc::DeviceType deviceType);
|
|
/// Add an entry to the 'async-only' attribute (clause spelled without
|
|
/// arguments) for each of the additional device types (or a none if it is
|
|
/// empty).
|
|
void addAsyncOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add a value to the 'async' with the current list of device types.
|
|
void addAsyncOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add an entry to the 'wait-only' attribute (clause spelled without
|
|
/// arguments) for each of the additional device types (or a none if it is
|
|
/// empty).
|
|
void addWaitOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add an array-like entry to the 'wait' with the current list of device
|
|
/// types.
|
|
void addWaitOperands(MLIRContext *, bool hasDevnum, mlir::ValueRange,
|
|
llvm::ArrayRef<DeviceType>);
|
|
}];
|
|
|
|
let assemblyFormat = [{
|
|
oilist(
|
|
`if` `(` $ifCond `)`
|
|
| `async` `` custom<DeviceTypeOperandsWithKeywordOnly>($asyncOperands,
|
|
type($asyncOperands), $asyncOperandsDeviceType, $asyncOnly)
|
|
| `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
|
|
| `wait` `` custom<WaitClause>($waitOperands, type($waitOperands),
|
|
$waitOperandsDeviceType, $waitOperandsSegments, $hasWaitDevnum,
|
|
$waitOnly)
|
|
)
|
|
$region attr-dict-with-keyword
|
|
}];
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
def OpenACC_TerminatorOp
|
|
: OpenACC_Op<"terminator", [Pure, Terminator,
|
|
DeclareOpInterfaceMethods<
|
|
RegionBranchTerminatorOpInterface,
|
|
["getMutableSuccessorOperands"]>]> {
|
|
let summary = "Generic terminator for OpenACC regions";
|
|
|
|
let description = [{
|
|
A terminator operation for regions that appear in the body of OpenACC
|
|
operation. Generic OpenACC construct regions are not expected to return any
|
|
value so the terminator takes no operands. The terminator op returns control
|
|
to the enclosing op.
|
|
}];
|
|
|
|
let assemblyFormat = "attr-dict";
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.6.6 Enter Data Directive
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_EnterDataOp : OpenACC_Op<"enter_data",
|
|
[AttrSizedOperandSegments,
|
|
MemoryEffects<[MemWrite<OpenACC_ConstructResource>,
|
|
MemRead<OpenACC_CurrentDeviceIdResource>]>]> {
|
|
let summary = "enter data operation";
|
|
|
|
let description = [{
|
|
The "acc.enter_data" operation represents the OpenACC enter data directive.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.enter_data create(%d1 : memref<10xf32>) attributes {async}
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins Optional<I1>:$ifCond,
|
|
Optional<IntOrIndex>:$asyncOperand,
|
|
UnitAttr:$async,
|
|
Optional<IntOrIndex>:$waitDevnum,
|
|
Variadic<IntOrIndex>:$waitOperands,
|
|
UnitAttr:$wait,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$dataClauseOperands);
|
|
|
|
let extraClassDeclaration = [{
|
|
/// The number of data operands.
|
|
unsigned getNumDataOperands();
|
|
|
|
/// The i-th data operand passed.
|
|
Value getDataOperand(unsigned i);
|
|
|
|
/// Add an entry to the 'async-only' attribute (clause spelled without
|
|
/// arguments). DeviceType array is supplied even though it should always be
|
|
/// empty, so this can mirror other versions of this function.
|
|
void addAsyncOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add a value to the 'async'. DeviceType array is supplied even though it
|
|
/// should always be empty, so this can mirror other versions of this
|
|
/// function.
|
|
void addAsyncOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add an entry to the 'wait-only' attribute (clause spelled without
|
|
/// arguments). DeviceType array is supplied even though it should always be
|
|
/// empty, so this can mirror other versions of this function.
|
|
void addWaitOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add an array-like entry to the 'wait'. DeviceType array is supplied
|
|
/// even though it should always be empty, so this can mirror other versions
|
|
/// of this function.
|
|
void addWaitOperands(MLIRContext *, bool hasDevnum, mlir::ValueRange,
|
|
llvm::ArrayRef<DeviceType>);
|
|
}];
|
|
|
|
let assemblyFormat = [{
|
|
oilist(
|
|
`if` `(` $ifCond `)`
|
|
| `async` `` custom<OperandWithKeywordOnly>($asyncOperand,
|
|
type($asyncOperand), $async)
|
|
| `wait_devnum` `(` $waitDevnum `:` type($waitDevnum) `)`
|
|
| `wait` `` custom<OperandsWithKeywordOnly>($waitOperands,
|
|
type($waitOperands), $wait)
|
|
| `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
|
|
)
|
|
attr-dict-with-keyword
|
|
}];
|
|
|
|
let hasCanonicalizer = 1;
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.6.6 Exit Data Directive
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_ExitDataOp : OpenACC_Op<"exit_data",
|
|
[AttrSizedOperandSegments,
|
|
MemoryEffects<[MemWrite<OpenACC_ConstructResource>,
|
|
MemRead<OpenACC_CurrentDeviceIdResource>]>]> {
|
|
let summary = "exit data operation";
|
|
|
|
let description = [{
|
|
The "acc.exit_data" operation represents the OpenACC exit data directive.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.exit_data delete(%d1 : memref<10xf32>) attributes {async}
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins Optional<I1>:$ifCond,
|
|
Optional<IntOrIndex>:$asyncOperand,
|
|
UnitAttr:$async,
|
|
Optional<IntOrIndex>:$waitDevnum,
|
|
Variadic<IntOrIndex>:$waitOperands,
|
|
UnitAttr:$wait,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$dataClauseOperands,
|
|
UnitAttr:$finalize);
|
|
|
|
let extraClassDeclaration = [{
|
|
/// The number of data operands.
|
|
unsigned getNumDataOperands();
|
|
|
|
/// The i-th data operand passed.
|
|
Value getDataOperand(unsigned i);
|
|
|
|
/// Add an entry to the 'async-only' attribute (clause spelled without
|
|
/// arguments). DeviceType array is supplied even though it should always be
|
|
/// empty, so this can mirror other versions of this function.
|
|
void addAsyncOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add a value to the 'async'. DeviceType array is supplied even though it
|
|
/// should always be empty, so this can mirror other versions of this
|
|
/// function.
|
|
void addAsyncOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
|
|
/// Add an entry to the 'wait-only' attribute (clause spelled without
|
|
/// arguments). DeviceType array is supplied even though it should always be
|
|
/// empty, so this can mirror other versions of this function.
|
|
void addWaitOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add an array-like entry to the 'wait'. DeviceType array is supplied
|
|
/// even though it should always be empty, so this can mirror other versions
|
|
/// of this function.
|
|
void addWaitOperands(MLIRContext *, bool hasDevnum, mlir::ValueRange,
|
|
llvm::ArrayRef<DeviceType>);
|
|
}];
|
|
|
|
let assemblyFormat = [{
|
|
oilist(
|
|
`if` `(` $ifCond `)`
|
|
| `async` `` custom<OperandWithKeywordOnly>($asyncOperand,
|
|
type($asyncOperand), $async)
|
|
| `wait_devnum` `(` $waitDevnum `:` type($waitDevnum) `)`
|
|
| `wait` `` custom<OperandsWithKeywordOnly>($waitOperands,
|
|
type($waitOperands), $wait)
|
|
| `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
|
|
)
|
|
attr-dict-with-keyword
|
|
}];
|
|
|
|
let hasCanonicalizer = 1;
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.8 Host_Data Construct
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_HostDataOp
|
|
: OpenACC_Op<"host_data",
|
|
[AttrSizedOperandSegments,
|
|
DeclareOpInterfaceMethods<RegionBranchOpInterface,
|
|
["getSuccessorInputs"]>,
|
|
MemoryEffects<[MemWrite<OpenACC_ConstructResource>,
|
|
MemRead<OpenACC_CurrentDeviceIdResource>]>]> {
|
|
let summary = "host_data construct";
|
|
|
|
let description = [{
|
|
The "acc.host_data" operation represents the OpenACC host_data construct.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
%0 = acc.use_device varPtr(%a : !llvm.ptr) -> !llvm.ptr
|
|
acc.host_data dataOperands(%0 : !llvm.ptr) {
|
|
|
|
}
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins Optional<I1>:$ifCond,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$dataClauseOperands,
|
|
UnitAttr:$ifPresent);
|
|
|
|
let regions = (region AnyRegion:$region);
|
|
|
|
let assemblyFormat = [{
|
|
oilist(
|
|
`if` `(` $ifCond `)`
|
|
| `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
|
|
)
|
|
$region attr-dict-with-keyword
|
|
}];
|
|
|
|
let hasVerifier = 1;
|
|
let hasCanonicalizer = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.9 loop Construct
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_LoopOp
|
|
: OpenACC_Op<
|
|
"loop", [AttrSizedOperandSegments, AutomaticAllocationScope,
|
|
RecursiveMemoryEffects,
|
|
DeclareOpInterfaceMethods<ComputeRegionOpInterface>,
|
|
DeclareOpInterfaceMethods<LoopLikeOpInterface>,
|
|
DeclareOpInterfaceMethods<RegionBranchOpInterface,
|
|
["getSuccessorInputs"]>,
|
|
MemoryEffects<[MemWrite<OpenACC_ConstructResource>]>]> {
|
|
let summary = "loop construct";
|
|
|
|
let description = [{
|
|
The `acc.loop` operation represents the OpenACC loop construct and when
|
|
bounds are included, the associated source language loop iterators. The
|
|
lower and upper bounds specify a half-open range: the range includes the
|
|
lower bound but does not include the upper bound. If the `inclusive`
|
|
attribute is set then the upper bound is included.
|
|
|
|
In cases where the OpenACC loop directive needs to capture multiple
|
|
source language loops, such as in the case of `collapse` or `tile`,
|
|
the multiple induction arguments are used to capture each case. Having
|
|
such a representation makes sure no intermediate transformation such
|
|
as Loop Invariant Code Motion breaks the property requested by the
|
|
clause on the loop constructs.
|
|
|
|
Each `acc.loop` holds private and reduction operands which are the
|
|
ssa values from the corresponding `acc.private` or `acc.reduction`
|
|
operations. Additionally, firstprivate operands are supported to
|
|
represent cases where privatization is needed with initialization
|
|
from an original value. While the OpenACC specification does not
|
|
explicitly support firstprivate on loop constructs, this extension
|
|
enables representing privatization scenarios that arise from an
|
|
optimization and codegen pipeline operating on acc dialect.
|
|
|
|
The operation supports capturing information that it comes combined
|
|
constructs (e.g., `parallel loop`, `kernels loop`, `serial loop`)
|
|
through the `combined` attribute despite requiring the `acc.loop`
|
|
to be decomposed from the compute operation representing compute
|
|
construct.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.loop gang() vector() (%arg3 : index, %arg4 : index, %arg5 : index) =
|
|
(%c0, %c0, %c0 : index, index, index) to
|
|
(%c10, %c10, %c10 : index, index, index) step
|
|
(%c1, %c1, %c1 : index, index, index) {
|
|
// Loop body
|
|
acc.yield
|
|
} attributes { collapse = [3] }
|
|
```
|
|
|
|
`collapse`, `gang`, `worker`, `vector`, `seq`, `independent`, `auto`,
|
|
`cache`, and `tile` operands are supported with `device_type`
|
|
information. These clauses should only be accessed through the provided
|
|
device-type-aware getter methods. When modifying these operands, the
|
|
corresponding `device_type` attributes must be updated to maintain
|
|
consistency between operands and their target device types.
|
|
|
|
The `unstructured` attribute indicates that the loops inside the OpenACC
|
|
construct contain early exits and cannot be lowered to structured MLIR
|
|
operations. When this flag is set, the acc.loop should have no induction
|
|
variables and the loop must be implemented via explicit control flow
|
|
inside its body.
|
|
}];
|
|
|
|
let arguments = (ins
|
|
Variadic<IntOrIndex>:$lowerbound,
|
|
Variadic<IntOrIndex>:$upperbound,
|
|
Variadic<IntOrIndex>:$step,
|
|
OptionalAttr<DenseBoolArrayAttr>:$inclusiveUpperbound,
|
|
OptionalAttr<I64ArrayAttr>:$collapse,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$collapseDeviceType,
|
|
Variadic<IntOrIndex>:$gangOperands,
|
|
OptionalAttr<GangArgTypeArrayAttr>:$gangOperandsArgType,
|
|
OptionalAttr<DenseI32ArrayAttr>:$gangOperandsSegments,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$gangOperandsDeviceType,
|
|
Variadic<IntOrIndex>:$workerNumOperands,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$workerNumOperandsDeviceType,
|
|
Variadic<IntOrIndex>:$vectorOperands,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$vectorOperandsDeviceType,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$seq,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$independent,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$auto_,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$gang,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$worker,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$vector,
|
|
Variadic<IntOrIndex>:$tileOperands,
|
|
OptionalAttr<DenseI32ArrayAttr>:$tileOperandsSegments,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$tileOperandsDeviceType,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$cacheOperands,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$privateOperands,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$firstprivateOperands,
|
|
Variadic<AnyType>:$reductionOperands,
|
|
OptionalAttr<OpenACC_CombinedConstructsAttr>:$combined,
|
|
UnitAttr:$unstructured
|
|
);
|
|
|
|
let results = (outs Variadic<AnyType>:$results);
|
|
|
|
let regions = (region AnyRegion:$region);
|
|
|
|
let extraClassDeclaration = [{
|
|
static StringRef getAutoAttrStrName() { return "auto"; }
|
|
static StringRef getGangNumKeyword() { return "num"; }
|
|
static StringRef getGangDimKeyword() { return "dim"; }
|
|
static StringRef getGangStaticKeyword() { return "static"; }
|
|
static StringRef getControlKeyword() { return "control"; }
|
|
|
|
/// The number of private and reduction operands.
|
|
unsigned getNumDataOperands();
|
|
|
|
/// The i-th data operand passed.
|
|
Value getDataOperand(unsigned i);
|
|
|
|
/// Used to retrieve the block inside the op's region.
|
|
Block &getBody() { return getLoopRegions().front()->front(); }
|
|
|
|
/// Used to determine if this operation is merely a container for a loop
|
|
/// operation instead of being loop-like itself.
|
|
bool isLoopLike() { return !getLowerbound().empty(); }
|
|
bool isContainerLike() { return !isLoopLike(); }
|
|
|
|
/// Return true if the op has the auto attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasAuto();
|
|
/// Return true if the op has the auto attribute for the given device_type.
|
|
bool hasAuto(mlir::acc::DeviceType deviceType);
|
|
/// Return true if the op has the independent attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasIndependent();
|
|
/// Return true if the op has the independent attribute for the given
|
|
/// device_type.
|
|
bool hasIndependent(mlir::acc::DeviceType deviceType);
|
|
/// Return true if the op has the seq attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasSeq();
|
|
/// Return true if the op has the seq attribute for the given device_type.
|
|
bool hasSeq(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return the value of the vector clause if present.
|
|
mlir::Value getVectorValue();
|
|
/// Return the value of the vector clause for the given device_type
|
|
/// if present.
|
|
mlir::Value getVectorValue(mlir::acc::DeviceType deviceType);
|
|
/// Return true if the op has the vector attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasVector();
|
|
/// Return true if the op has the vector attribute for the given
|
|
/// device_type.
|
|
bool hasVector(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return the value of the worker clause if present.
|
|
mlir::Value getWorkerValue();
|
|
/// Return the value of the worker clause for the given device_type
|
|
/// if present.
|
|
mlir::Value getWorkerValue(mlir::acc::DeviceType deviceType);
|
|
/// Return true if the op has the worker attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasWorker();
|
|
/// Return true if the op has the worker attribute for the given
|
|
/// device_type.
|
|
bool hasWorker(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return the values of the tile clause if present.
|
|
mlir::Operation::operand_range getTileValues();
|
|
/// Return the values of the tile clause for the given device_type if
|
|
/// present.
|
|
mlir::Operation::operand_range
|
|
getTileValues(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return the value of the collapse clause if present.
|
|
std::optional<int64_t> getCollapseValue();
|
|
/// Return the value of the collapse clause for the given device_type
|
|
/// if present.
|
|
std::optional<int64_t> getCollapseValue(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return true if the op has the gang attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasGang();
|
|
/// Return true if the op has the gang attribute for the given
|
|
/// device_type.
|
|
bool hasGang(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return the value of the worker clause if present.
|
|
mlir::Value getGangValue(mlir::acc::GangArgType gangArgType);
|
|
/// Return the value of the worker clause for the given device_type
|
|
/// if present.
|
|
mlir::Value getGangValue(mlir::acc::GangArgType gangArgType, mlir::acc::DeviceType deviceType);
|
|
|
|
// Add an entry to the 'seq' attribute for each additional device types.
|
|
void addSeq(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
// Add an entry to the 'independent' attribute for each additional device
|
|
// types.
|
|
void addIndependent(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
// Add an entry to the 'auto' attribute for each additional device types.
|
|
void addAuto(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
|
|
// Sets the collapse value for this 'loop' for a set of DeviceTypes. Note
|
|
// that this may only be set once per DeviceType, and will fail the verifier
|
|
// if this is set multiple times.
|
|
void setCollapseForDeviceTypes(MLIRContext *, llvm::ArrayRef<DeviceType>,
|
|
llvm::APInt);
|
|
// Sets the tile values for this 'loop' for a set of DeviceTypes. All of the
|
|
// values should be integral constants, with the '*' represented as a '-1'.
|
|
void setTileForDeviceTypes(MLIRContext *, llvm::ArrayRef<DeviceType>,
|
|
mlir::ValueRange);
|
|
|
|
// Add a value to the 'vector' list with a current list of device_types.
|
|
void addVectorOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
// Add an empty value to the 'vector' list with a current list of
|
|
// device_types. This is for the case where there is no expression specified
|
|
// in a 'vector'.
|
|
void addEmptyVector(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
// Add a value to the 'worker' list with a current list of device_types.
|
|
void addWorkerNumOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
// Add an empty value to the 'worker' list with a current list of
|
|
// device_types. This is for the case where there is no expression specified
|
|
// in a 'worker'.
|
|
void addEmptyWorker(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
|
|
// Adds a collection of operands for a 'gang' clause that has various types
|
|
// corresponding to each operand.
|
|
void addGangOperands(MLIRContext *, llvm::ArrayRef<DeviceType>,
|
|
llvm::ArrayRef<GangArgType>, mlir::ValueRange);
|
|
|
|
// Add an empty value to the 'gang' list with a current list of
|
|
// device_types. This is for the case where there is no expression specified
|
|
// in a 'gang'.
|
|
void addEmptyGang(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
|
|
// Return whether this LoopOp has an auto, seq, or independent for the
|
|
// specified device-type.
|
|
bool hasParallelismFlag(DeviceType);
|
|
|
|
// Return whether this LoopOp has a gang, worker, or vector applying to the
|
|
// 'default'/None device-type.
|
|
bool hasDefaultGangWorkerVector();
|
|
|
|
// Return whether this LoopOp has a gang, worker, or vector for the given
|
|
// device-type.
|
|
bool hasAnyGangWorkerVector(DeviceType deviceType);
|
|
|
|
// Used to obtain the parallelism mode for the requested device type.
|
|
// This first checks if the mode is set for the device_type requested.
|
|
// And if not, it returns the non-device_type mode.
|
|
LoopParMode getDefaultOrDeviceTypeParallelism(DeviceType);
|
|
|
|
/// Adds a private clause variable to this operation, including its recipe.
|
|
void addPrivatization(MLIRContext *, mlir::acc::PrivateOp op,
|
|
mlir::acc::PrivateRecipeOp recipe);
|
|
/// Adds a firstprivate clause variable to this operation, including its
|
|
/// recipe.
|
|
void addFirstPrivatization(MLIRContext *, mlir::acc::FirstprivateOp op,
|
|
mlir::acc::FirstprivateRecipeOp recipe);
|
|
/// Adds a reduction clause variable to this operation, including its
|
|
/// recipe.
|
|
void addReduction(MLIRContext *, mlir::acc::ReductionOp op,
|
|
mlir::acc::ReductionRecipeOp recipe);
|
|
}];
|
|
|
|
let hasCustomAssemblyFormat = 1;
|
|
let assemblyFormat = [{
|
|
( `combined` `(` custom<CombinedConstructsLoop>($combined)^ `)` )?
|
|
oilist(
|
|
`gang` `` custom<GangClause>($gangOperands, type($gangOperands),
|
|
$gangOperandsArgType, $gangOperandsDeviceType,
|
|
$gangOperandsSegments, $gang)
|
|
| `worker` `` custom<DeviceTypeOperandsWithKeywordOnly>(
|
|
$workerNumOperands, type($workerNumOperands),
|
|
$workerNumOperandsDeviceType, $worker)
|
|
| `vector` `` custom<DeviceTypeOperandsWithKeywordOnly>($vectorOperands,
|
|
type($vectorOperands), $vectorOperandsDeviceType, $vector)
|
|
| `private` `(` $privateOperands `:` type($privateOperands) `)`
|
|
| `firstprivate` `(` $firstprivateOperands `:` type($firstprivateOperands) `)`
|
|
| `tile` `(` custom<DeviceTypeOperandsWithSegment>($tileOperands,
|
|
type($tileOperands), $tileOperandsDeviceType, $tileOperandsSegments)
|
|
`)`
|
|
| `reduction` `(` $reductionOperands `:` type($reductionOperands) `)`
|
|
| `cache` `(` $cacheOperands `:` type($cacheOperands) `)`
|
|
)
|
|
custom<LoopControl>($region, $lowerbound, type($lowerbound), $upperbound,
|
|
type($upperbound), $step, type($step))
|
|
( `(` type($results)^ `)` )?
|
|
attr-dict-with-keyword
|
|
}];
|
|
|
|
let hasVerifier = 1;
|
|
|
|
let builders = [
|
|
OpBuilder<(ins "::mlir::ValueRange":$lowerbounds,
|
|
"::mlir::ValueRange":$upperbounds,
|
|
"::mlir::ValueRange":$steps,
|
|
"LoopParMode":$parMode), [{
|
|
auto deviceNoneAttr = mlir::acc::DeviceTypeAttr::get(
|
|
$_builder.getContext(), mlir::acc::DeviceType::None);
|
|
auto arrOfDeviceNone = mlir::ArrayAttr::get(
|
|
$_builder.getContext(), deviceNoneAttr);
|
|
build($_builder, $_state,
|
|
/*results=*/{},
|
|
/*lowerbound=*/lowerbounds,
|
|
/*upperbound=*/upperbounds,
|
|
/*step=*/steps,
|
|
/*inclusiveUpperbound=*/nullptr,
|
|
/*collapse=*/nullptr,
|
|
/*collapseDeviceType=*/nullptr,
|
|
/*gangOperands=*/{},
|
|
/*gangOperandsArgType=*/nullptr,
|
|
/*gangOperandsSegments=*/nullptr,
|
|
/*gangOperandsDeviceType=*/nullptr,
|
|
/*workerNumOperands=*/{},
|
|
/*workerNumOperandsDeviceType=*/nullptr,
|
|
/*vectorOperands=*/{},
|
|
/*vectorOperandsDeviceType=*/nullptr,
|
|
/*seq=*/parMode == LoopParMode::loop_seq ?
|
|
arrOfDeviceNone : nullptr,
|
|
/*independent=*/parMode == LoopParMode::loop_independent ?
|
|
arrOfDeviceNone : nullptr,
|
|
/*auto_=*/parMode == LoopParMode::loop_auto ?
|
|
arrOfDeviceNone : nullptr,
|
|
/*gang=*/nullptr,
|
|
/*worker=*/nullptr,
|
|
/*vector=*/nullptr,
|
|
/*tileOperands=*/{},
|
|
/*tileOperandsSegments=*/nullptr,
|
|
/*tileOperandsDeviceType=*/nullptr,
|
|
/*cacheOperands=*/{},
|
|
/*privateOperands=*/{},
|
|
/*firstprivateOperands=*/{},
|
|
/*reductionOperands=*/{},
|
|
/*combined=*/nullptr);
|
|
}]
|
|
>
|
|
];
|
|
}
|
|
|
|
// Yield operation for the acc.loop and acc.parallel operations.
|
|
def OpenACC_YieldOp : OpenACC_Op<"yield", [Pure, ReturnLike, Terminator,
|
|
ParentOneOf<["FirstprivateRecipeOp, LoopOp, ParallelOp, PrivateRecipeOp,"
|
|
"ReductionRecipeOp, ReductionInitOp, ReductionCombineRegionOp,"
|
|
"SerialOp, AtomicUpdateOp, ComputeRegionOp"]>]> {
|
|
let summary = "Acc yield and termination operation";
|
|
|
|
let description = [{
|
|
`acc.yield` is a special terminator operation for block inside regions in
|
|
various acc ops (including parallel, loop, atomic.update). It returns values
|
|
to the immediately enclosing acc op.
|
|
}];
|
|
|
|
let arguments = (ins Variadic<AnyType>:$operands);
|
|
|
|
let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>];
|
|
|
|
let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?";
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.12 atomic construct
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def AtomicReadOp : OpenACC_Op<"atomic.read", [AtomicReadOpInterface]> {
|
|
|
|
let summary = "performs an atomic read";
|
|
|
|
let description = [{
|
|
This operation performs an atomic read.
|
|
|
|
The operand `x` is the address from where the value is atomically read.
|
|
The operand `v` is the address where the value is stored after reading.
|
|
}];
|
|
|
|
let arguments = (ins OpenACC_PointerLikeType:$x,
|
|
OpenACC_PointerLikeType:$v,
|
|
TypeAttr:$element_type, Optional<I1>:$ifCond);
|
|
let assemblyFormat = [{
|
|
oilist(
|
|
`if` `(` $ifCond `)`
|
|
)
|
|
$v `=` $x
|
|
`:` type($v) `,` type($x) `,` $element_type
|
|
attr-dict
|
|
}];
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
def AtomicWriteOp : OpenACC_Op<"atomic.write",[AtomicWriteOpInterface]> {
|
|
|
|
let summary = "performs an atomic write";
|
|
|
|
let description = [{
|
|
This operation performs an atomic write.
|
|
|
|
The operand `x` is the address to where the `expr` is atomically
|
|
written w.r.t. multiple threads. The evaluation of `expr` need not be
|
|
atomic w.r.t. the write to address. In general, the type(x) must
|
|
dereference to type(expr).
|
|
}];
|
|
|
|
let arguments = (ins OpenACC_PointerLikeType:$x,
|
|
AnyType:$expr,
|
|
Optional<I1>:$ifCond);
|
|
let assemblyFormat = [{
|
|
oilist(
|
|
`if` `(` $ifCond `)`
|
|
)
|
|
$x `=` $expr
|
|
`:` type($x) `,` type($expr)
|
|
attr-dict
|
|
}];
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
def AtomicUpdateOp : OpenACC_Op<"atomic.update",
|
|
[SingleBlockImplicitTerminator<"YieldOp">,
|
|
RecursiveMemoryEffects,
|
|
AtomicUpdateOpInterface]> {
|
|
|
|
let summary = "performs an atomic update";
|
|
|
|
let description = [{
|
|
This operation performs an atomic update.
|
|
|
|
The operand `x` is exactly the same as the operand `x` in the OpenACC
|
|
Standard (OpenACC 3.3, section 2.12). It is the address of the variable
|
|
that is being updated. `x` is atomically read/written.
|
|
|
|
The region describes how to update the value of `x`. It takes the value at
|
|
`x` as an input and must yield the updated value. Only the update to `x` is
|
|
atomic. Generally the region must have only one instruction, but can
|
|
potentially have more than one instructions too. The update is sematically
|
|
similar to a compare-exchange loop based atomic update.
|
|
|
|
The syntax of atomic update operation is different from atomic read and
|
|
atomic write operations. This is because only the host dialect knows how to
|
|
appropriately update a value. For example, while generating LLVM IR, if
|
|
there are no special `atomicrmw` instructions for the operation-type
|
|
combination in atomic update, a compare-exchange loop is generated, where
|
|
the core update operation is directly translated like regular operations by
|
|
the host dialect. The front-end must handle semantic checks for allowed
|
|
operations.
|
|
}];
|
|
|
|
let arguments = (ins Arg<OpenACC_PointerLikeType,
|
|
"Address of variable to be updated",
|
|
[MemRead, MemWrite]>:$x,
|
|
Optional<I1>:$ifCond);
|
|
let regions = (region SizedRegion<1>:$region);
|
|
let assemblyFormat = [{
|
|
oilist(
|
|
`if` `(` $ifCond `)`
|
|
)
|
|
$x `:` type($x)
|
|
$region attr-dict
|
|
}];
|
|
let hasVerifier = 1;
|
|
let hasRegionVerifier = 1;
|
|
let hasCanonicalizeMethod = 1;
|
|
let extraClassDeclaration = [{
|
|
Operation* getFirstOp() {
|
|
return &getRegion().front().getOperations().front();
|
|
}
|
|
}];
|
|
}
|
|
|
|
def AtomicCaptureOp : OpenACC_Op<"atomic.capture",
|
|
[SingleBlockImplicitTerminator<"TerminatorOp">,
|
|
RecursiveMemoryEffects, AtomicCaptureOpInterface]> {
|
|
let summary = "performs an atomic capture";
|
|
let description = [{
|
|
This operation performs an atomic capture.
|
|
|
|
The region has the following allowed forms:
|
|
|
|
```
|
|
acc.atomic.capture {
|
|
acc.atomic.update ...
|
|
acc.atomic.read ...
|
|
acc.terminator
|
|
}
|
|
|
|
acc.atomic.capture {
|
|
acc.atomic.read ...
|
|
acc.atomic.update ...
|
|
acc.terminator
|
|
}
|
|
|
|
acc.atomic.capture {
|
|
acc.atomic.read ...
|
|
acc.atomic.write ...
|
|
acc.terminator
|
|
}
|
|
```
|
|
|
|
}];
|
|
|
|
let arguments = (ins Optional<I1>:$ifCond);
|
|
|
|
let regions = (region SizedRegion<1>:$region);
|
|
let assemblyFormat = [{
|
|
oilist(
|
|
`if` `(` $ifCond `)`
|
|
)
|
|
$region attr-dict
|
|
}];
|
|
let hasRegionVerifier = 1;
|
|
let extraClassDeclaration = [{
|
|
/// Returns the `atomic.read` operation inside the region, if any.
|
|
/// Otherwise, it returns nullptr.
|
|
AtomicReadOp getAtomicReadOp();
|
|
|
|
/// Returns the `atomic.write` operation inside the region, if any.
|
|
/// Otherwise, it returns nullptr.
|
|
AtomicWriteOp getAtomicWriteOp();
|
|
|
|
/// Returns the `atomic.update` operation inside the region, if any.
|
|
/// Otherwise, it returns nullptr.
|
|
AtomicUpdateOp getAtomicUpdateOp();
|
|
}];
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.13 Declare Directive
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_DeclareEnterOp : OpenACC_Op<"declare_enter",
|
|
[MemoryEffects<[MemWrite<OpenACC_ConstructResource>,
|
|
MemRead<OpenACC_CurrentDeviceIdResource>]>]> {
|
|
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<OpenACC_AnyPointerOrMappableType>:$dataClauseOperands);
|
|
let results = (outs OpenACC_DeclareTokenType:$token);
|
|
|
|
let assemblyFormat = [{
|
|
oilist(
|
|
`dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
|
|
)
|
|
attr-dict-with-keyword
|
|
}];
|
|
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
def OpenACC_DeclareExitOp : OpenACC_Op<"declare_exit",
|
|
[AttrSizedOperandSegments,
|
|
MemoryEffects<[MemWrite<OpenACC_ConstructResource>,
|
|
MemRead<OpenACC_CurrentDeviceIdResource>]>]> {
|
|
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 = #acc<data_clause declare_device_resident>}
|
|
acc.declare_exit dataOperands(%0 : !llvm.ptr)
|
|
acc.delete accPtr(%0 : !llvm.ptr) {dataClause = #acc<data_clause declare_device_resident>}
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins
|
|
Optional<OpenACC_DeclareTokenType>:$token,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$dataClauseOperands);
|
|
|
|
let assemblyFormat = [{
|
|
oilist(
|
|
`token` `(` $token `)` |
|
|
`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 = #acc<data_clause create>}
|
|
acc.declare_exit dataOperands(%1 : !llvm.ptr)
|
|
acc.delete accPtr(%1 : !llvm.ptr) {dataClause = #acc<data_clause create>}
|
|
}
|
|
```
|
|
}];
|
|
|
|
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_DeclareOp : OpenACC_Op<"declare",
|
|
[RecursiveMemoryEffects,
|
|
MemoryEffects<[MemWrite<OpenACC_ConstructResource>]>]> {
|
|
let summary = "declare implicit region";
|
|
|
|
let description = [{
|
|
The "acc.declare" operation represents an implicit declare region in
|
|
function (and subroutine in Fortran).
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
%pa = acc.present varPtr(%a : memref<10x10xf32>) -> memref<10x10xf32>
|
|
acc.declare dataOperands(%pa: memref<10x10xf32>) {
|
|
// implicit region
|
|
}
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$dataClauseOperands);
|
|
|
|
let regions = (region AnyRegion:$region);
|
|
|
|
let assemblyFormat = [{
|
|
`dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
|
|
$region attr-dict-with-keyword
|
|
}];
|
|
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.15.1 Routine Directive
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_RoutineOp : OpenACC_Op<"routine", [IsolatedFromAbove]> {
|
|
let summary = "acc routine operation";
|
|
|
|
let description = [{
|
|
The `acc.routine` operation is used to capture the clauses of acc
|
|
routine directive, including the associated function name. The associated
|
|
function keeps track of its corresponding routine declaration through
|
|
the `RoutineInfoAttr`.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
func.func @acc_func(%a : i64) -> () attributes
|
|
{acc.routine_info = #acc.routine_info<[@acc_func_rout1]>} {
|
|
return
|
|
}
|
|
acc.routine @acc_func_rout1 func(@acc_func) gang
|
|
```
|
|
|
|
`bind`, `gang`, `worker`, `vector` and `seq` operands are supported with
|
|
`device_type` information. They should only be accessed by the extra
|
|
provided getters. If modified, the corresponding `device_type` attributes
|
|
must be modified as well.
|
|
}];
|
|
|
|
let arguments = (ins SymbolNameAttr:$sym_name, SymbolRefAttr:$func_name,
|
|
OptionalAttr<SymbolRefArrayAttr>:$bindIdName,
|
|
OptionalAttr<StrArrayAttr>:$bindStrName,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$bindIdNameDeviceType,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$bindStrNameDeviceType,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$worker,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$vector,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$seq, UnitAttr:$nohost,
|
|
UnitAttr:$implicit, OptionalAttr<DeviceTypeArrayAttr>:$gang,
|
|
OptionalAttr<I64ArrayAttr>:$gangDim,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$gangDimDeviceType);
|
|
|
|
let extraClassDeclaration = [{
|
|
// 'create' function to generate an 'empty' routine.
|
|
static RoutineOp create(::mlir::OpBuilder & builder,
|
|
::mlir::Location location,
|
|
::llvm::StringRef sym_name,
|
|
mlir::SymbolRefAttr func_name, bool implicit) {
|
|
return create(builder, location, sym_name, func_name, /*bindIDName=*/{},
|
|
/*bindStrName=*/{}, /*bindIdNameDeviceType=*/{},
|
|
/*bindStrnameDeviceType=*/{}, /*worker=*/{}, /*vector=*/{},
|
|
/*seq=*/{}, /*nohost=*/false, implicit, /*gang=*/{},
|
|
/*gangDim=*/{}, /*gangDimDeviceType=*/{});
|
|
}
|
|
|
|
static StringRef getGangDimKeyword() { return "dim"; }
|
|
|
|
/// Return true if the op has the worker attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasWorker();
|
|
/// Return true if the op has the worker attribute for the given
|
|
/// device_type.
|
|
bool hasWorker(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return true if the op has the vector attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasVector();
|
|
/// Return true if the op has the vector attribute for the given
|
|
/// device_type.
|
|
bool hasVector(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return true if the op has the seq attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasSeq();
|
|
/// Return true if the op has the seq attribute for the given
|
|
/// device_type.
|
|
bool hasSeq(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return true if the op has the gang attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasGang();
|
|
/// Return true if the op has the gang attribute for the given
|
|
/// device_type.
|
|
bool hasGang(mlir::acc::DeviceType deviceType);
|
|
|
|
std::optional<int64_t> getGangDimValue();
|
|
std::optional<int64_t> getGangDimValue(mlir::acc::DeviceType deviceType);
|
|
|
|
std::optional<::std::variant<mlir::SymbolRefAttr, mlir::StringAttr>> getBindNameValue();
|
|
std::optional<::std::variant<mlir::SymbolRefAttr, mlir::StringAttr>> getBindNameValue(mlir::acc::DeviceType deviceType);
|
|
|
|
// Add an entry to the 'seq' attribute for each additional device types.
|
|
void addSeq(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
// Add an entry to the 'vector' attribute for each additional device types.
|
|
void addVector(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
// Add an entry to the 'worker' attribute for each additional device types.
|
|
void addWorker(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
// Add an entry to the 'gang' attribute for each additional device type.
|
|
void addGang(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
// Add an entry to the 'gang' attribute with a value for each additional
|
|
// device type.
|
|
void addGang(MLIRContext *, llvm::ArrayRef<DeviceType>, uint64_t);
|
|
// Add an entry to the 'bind' string-name attribute for each additional
|
|
// device_type.
|
|
void addBindStrName(MLIRContext *, llvm::ArrayRef<DeviceType>,
|
|
mlir::StringAttr);
|
|
// Add an entry to the 'bind' ID-name attribute for each additional
|
|
// device_type.
|
|
void addBindIDName(MLIRContext *, llvm::ArrayRef<DeviceType>,
|
|
mlir::SymbolRefAttr);
|
|
}];
|
|
|
|
let assemblyFormat = [{
|
|
$sym_name `func` `(` $func_name `)`
|
|
oilist (
|
|
`bind` `(` custom<BindName>($bindIdName, $bindStrName ,$bindIdNameDeviceType, $bindStrNameDeviceType) `)`
|
|
| `gang` `` custom<RoutineGangClause>($gang, $gangDim, $gangDimDeviceType)
|
|
| `worker` custom<DeviceTypeArrayAttr>($worker)
|
|
| `vector` custom<DeviceTypeArrayAttr>($vector)
|
|
| `seq` custom<DeviceTypeArrayAttr>($seq)
|
|
| `nohost` $nohost
|
|
| `implicit` $implicit
|
|
) attr-dict-with-keyword
|
|
}];
|
|
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
def RoutineInfoAttr : OpenACC_Attr<"RoutineInfo", "routine_info"> {
|
|
let summary = "Keeps track of associated acc routine information";
|
|
|
|
let description = [{
|
|
This attribute is used to create the association between a function and
|
|
its `acc.routine` operation. A `func.func` uses this if its name
|
|
was referenced in an `acc routine` directive.
|
|
}];
|
|
|
|
let parameters = (ins ArrayRefParameter<"SymbolRefAttr", "">:$accRoutines);
|
|
let assemblyFormat = "`<` `[` `` $accRoutines `]` `>`";
|
|
}
|
|
|
|
def SpecializedRoutineAttr : OpenACC_Attr<"SpecializedRoutine",
|
|
"specialized_routine"> {
|
|
let summary = "Marks a specialized device version of an acc routine";
|
|
|
|
let description = [{
|
|
This attribute is attached to a function that was specialized from a host
|
|
function marked with `acc.routine_info`. It captures the parallelism level,
|
|
a reference to the original `acc.routine` operation, and the original
|
|
function name (since the specialized function may be renamed).
|
|
|
|
Example - before specialization:
|
|
```mlir
|
|
acc.routine @routine_gang func(@foo) gang
|
|
acc.routine @routine_vector func(@foo) vector
|
|
|
|
func.func @foo() attributes {
|
|
acc.routine_info = #acc.routine_info<[@routine_gang, @routine_vector]>
|
|
} { ... }
|
|
```
|
|
|
|
After specialization, there are three functions: the original function and
|
|
two specialized versions (one per parallelism level):
|
|
```mlir
|
|
acc.routine @routine_gang func(@foo) gang
|
|
acc.routine @routine_vector func(@foo) vector
|
|
|
|
// Original function (unchanged)
|
|
func.func @foo() attributes {
|
|
acc.routine_info = #acc.routine_info<[@routine_gang, @routine_vector]>
|
|
} { ... }
|
|
|
|
// Specialized for gang parallelism
|
|
func.func @foo_gang() attributes {
|
|
acc.specialized_routine = #acc.specialized_routine<@routine_gang, <gang_dim1>, "foo">
|
|
} { ... }
|
|
|
|
// Specialized for vector parallelism
|
|
func.func @foo_vector() attributes {
|
|
acc.specialized_routine = #acc.specialized_routine<@routine_vector, <vector>, "foo">
|
|
} { ... }
|
|
```
|
|
}];
|
|
|
|
let parameters = (ins
|
|
"SymbolRefAttr":$routine,
|
|
"ParLevelAttr":$level,
|
|
"StringAttr":$funcName
|
|
);
|
|
|
|
let assemblyFormat = "`<` $routine `,` $level `,` $funcName `>`";
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.14.1. Init Directive
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_InitOp : OpenACC_Op<"init", [AttrSizedOperandSegments]> {
|
|
let summary = "init operation";
|
|
|
|
let description = [{
|
|
The "acc.init" operation represents the OpenACC init executable
|
|
directive.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.init
|
|
acc.init device_num(%dev1 : i32)
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins OptionalAttr<TypedArrayAttrBase<OpenACC_DeviceTypeAttr, "Device type attributes">>:$device_types,
|
|
Optional<IntOrIndex>:$deviceNum,
|
|
Optional<I1>:$ifCond);
|
|
|
|
let extraClassDeclaration = [{
|
|
/// Adds a device type to the list of device types for this directive.
|
|
void addDeviceType(MLIRContext *, mlir::acc::DeviceType);
|
|
}];
|
|
|
|
let assemblyFormat = [{
|
|
oilist(`device_num` `(` $deviceNum `:` type($deviceNum) `)`
|
|
| `if` `(` $ifCond `)`
|
|
) attr-dict-with-keyword
|
|
}];
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.14.2. Shutdown
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_ShutdownOp : OpenACC_Op<"shutdown", [AttrSizedOperandSegments]> {
|
|
let summary = "shutdown operation";
|
|
|
|
let description = [{
|
|
The "acc.shutdown" operation represents the OpenACC shutdown executable
|
|
directive.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.shutdown
|
|
acc.shutdown device_num(%dev1 : i32)
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins OptionalAttr<TypedArrayAttrBase<OpenACC_DeviceTypeAttr, "Device type attributes">>:$device_types,
|
|
Optional<IntOrIndex>:$deviceNum,
|
|
Optional<I1>:$ifCond);
|
|
|
|
let extraClassDeclaration = [{
|
|
/// Adds a device type to the list of device types for this directive.
|
|
void addDeviceType(MLIRContext *, mlir::acc::DeviceType);
|
|
}];
|
|
|
|
let assemblyFormat = [{
|
|
oilist(`device_num` `(` $deviceNum `:` type($deviceNum) `)`
|
|
|`if` `(` $ifCond `)`
|
|
) attr-dict-with-keyword
|
|
}];
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.14.3. Set
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_SetOp : OpenACC_Op<"set", [AttrSizedOperandSegments,
|
|
MemoryEffects<[MemWrite<OpenACC_CurrentDeviceIdResource>]>]> {
|
|
let summary = "set operation";
|
|
|
|
let description = [{
|
|
The "acc.set" operation represents the OpenACC set directive.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.set device_num(%dev1 : i32)
|
|
```
|
|
}];
|
|
|
|
let arguments = (ins OptionalAttr<OpenACC_DeviceTypeAttr>:$device_type,
|
|
Optional<IntOrIndex>:$defaultAsync,
|
|
Optional<IntOrIndex>:$deviceNum,
|
|
Optional<I1>:$ifCond);
|
|
|
|
let assemblyFormat = [{
|
|
oilist(`default_async` `(` $defaultAsync `:` type($defaultAsync) `)`
|
|
| `device_num` `(` $deviceNum `:` type($deviceNum) `)`
|
|
| `if` `(` $ifCond `)`
|
|
) attr-dict-with-keyword
|
|
}];
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.14.4. Update Directive
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_UpdateOp : OpenACC_Op<"update",
|
|
[AttrSizedOperandSegments,
|
|
MemoryEffects<[MemWrite<OpenACC_ConstructResource>,
|
|
MemRead<OpenACC_CurrentDeviceIdResource>]>]> {
|
|
let summary = "update operation";
|
|
|
|
let description = [{
|
|
The `acc.update` operation represents the OpenACC update executable
|
|
directive.
|
|
As host and self clauses are synonyms, any operands for host and self are
|
|
add to $hostOperands.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.update device(%d1 : memref<10xf32>) attributes {async}
|
|
```
|
|
|
|
`async` and `wait` operands are supported with `device_type` information.
|
|
They should only be accessed by the extra provided getters. If modified,
|
|
the corresponding `device_type` attributes must be modified as well.
|
|
}];
|
|
|
|
let arguments = (ins Optional<I1>:$ifCond,
|
|
Variadic<IntOrIndex>:$asyncOperands,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOperandsDeviceType,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$asyncOnly,
|
|
Variadic<IntOrIndex>:$waitOperands,
|
|
OptionalAttr<DenseI32ArrayAttr>:$waitOperandsSegments,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$waitOperandsDeviceType,
|
|
OptionalAttr<BoolArrayAttr>:$hasWaitDevnum,
|
|
OptionalAttr<DeviceTypeArrayAttr>:$waitOnly,
|
|
Variadic<OpenACC_AnyPointerOrMappableType>:$dataClauseOperands,
|
|
UnitAttr:$ifPresent);
|
|
|
|
let extraClassDeclaration = [{
|
|
/// The number of data operands.
|
|
unsigned getNumDataOperands();
|
|
|
|
/// The i-th data operand passed.
|
|
Value getDataOperand(unsigned i);
|
|
|
|
/// Return true if the op has the async attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasAsyncOnly();
|
|
/// Return true if the op has the async attribute for the given device_type.
|
|
bool hasAsyncOnly(mlir::acc::DeviceType deviceType);
|
|
/// Return the value of the async clause if present.
|
|
mlir::Value getAsyncValue();
|
|
/// Return the value of the async clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getAsyncValue(mlir::acc::DeviceType deviceType);
|
|
|
|
/// Return true if the op has the wait attribute for the
|
|
/// mlir::acc::DeviceType::None device_type.
|
|
bool hasWaitOnly();
|
|
/// Return true if the op has the wait attribute for the given device_type.
|
|
bool hasWaitOnly(mlir::acc::DeviceType deviceType);
|
|
/// Return the values of the wait clause if present.
|
|
mlir::Operation::operand_range getWaitValues();
|
|
/// Return the values of the wait clause for the given device_type if
|
|
/// present.
|
|
mlir::Operation::operand_range
|
|
getWaitValues(mlir::acc::DeviceType deviceType);
|
|
/// Return the wait devnum value clause if present;
|
|
mlir::Value getWaitDevnum();
|
|
/// Return the wait devnum value clause for the given device_type if
|
|
/// present.
|
|
mlir::Value getWaitDevnum(mlir::acc::DeviceType deviceType);
|
|
/// Add an entry to the 'async-only' attribute (clause spelled without
|
|
/// arguments)for each of the additional device types (or a none if it is
|
|
/// empty).
|
|
void addAsyncOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add a value to the 'async' with the current list of device types.
|
|
void addAsyncOperand(MLIRContext *, mlir::Value,
|
|
llvm::ArrayRef<DeviceType>);
|
|
/// Add an entry to the 'wait-only' attribute (clause spelled without
|
|
/// arguments)for each of the additional device types (or a none if it is
|
|
/// empty).
|
|
void addWaitOnly(MLIRContext *, llvm::ArrayRef<DeviceType>);
|
|
/// Add an array-like entry to the 'wait' with the current list of device
|
|
/// types.
|
|
void addWaitOperands(MLIRContext *, bool hasDevnum, mlir::ValueRange,
|
|
llvm::ArrayRef<DeviceType>);
|
|
}];
|
|
|
|
let assemblyFormat = [{
|
|
oilist(
|
|
`if` `(` $ifCond `)`
|
|
| `async` `` custom<DeviceTypeOperandsWithKeywordOnly>($asyncOperands,
|
|
type($asyncOperands), $asyncOperandsDeviceType, $asyncOnly)
|
|
| `wait` `` custom<WaitClause>($waitOperands, type($waitOperands),
|
|
$waitOperandsDeviceType, $waitOperandsSegments, $hasWaitDevnum,
|
|
$waitOnly)
|
|
| `dataOperands` `(` $dataClauseOperands `:` type($dataClauseOperands) `)`
|
|
)
|
|
attr-dict-with-keyword
|
|
}];
|
|
|
|
let hasCanonicalizer = 1;
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 2.16.3. Wait Directive
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def OpenACC_WaitOp : OpenACC_Op<"wait", [AttrSizedOperandSegments]> {
|
|
let summary = "wait operation";
|
|
|
|
let description = [{
|
|
The "acc.wait" operation represents the OpenACC wait executable
|
|
directive.
|
|
|
|
Example:
|
|
|
|
```mlir
|
|
acc.wait(%value1: index)
|
|
acc.wait() async(%async1: i32)
|
|
```
|
|
|
|
acc.wait does not implement MemoryEffects interface,
|
|
so it affects all the resources. This is conservatively
|
|
correct. More precise modelling of the memory effects
|
|
seems to be impossible without the whole program analysis.
|
|
}];
|
|
|
|
let arguments = (ins Variadic<IntOrIndex>:$waitOperands,
|
|
Optional<IntOrIndex>:$asyncOperand,
|
|
Optional<IntOrIndex>:$waitDevnum,
|
|
UnitAttr:$async,
|
|
Optional<I1>:$ifCond);
|
|
|
|
let assemblyFormat = [{
|
|
( `(` $waitOperands^ `:` type($waitOperands) `)` )?
|
|
oilist(
|
|
`async` `` custom<OperandWithKeywordOnly>($asyncOperand,
|
|
type($asyncOperand), $async)
|
|
| `wait_devnum` `(` $waitDevnum `:` type($waitDevnum) `)`
|
|
| `if` `(` $ifCond `)`
|
|
) attr-dict-with-keyword
|
|
}];
|
|
let hasVerifier = 1;
|
|
}
|
|
|
|
include "mlir/Dialect/OpenACC/OpenACCCGAttributes.td"
|
|
include "mlir/Dialect/OpenACC/OpenACCCGOps.td"
|
|
|
|
#endif // OPENACC_OPS
|