21#include "llvm/ADT/STLExtras.h"
22#include "llvm/ADT/SetVector.h"
23#include "llvm/ADT/TypeSwitch.h"
24#include "llvm/IR/Intrinsics.h"
25#include "llvm/Support/Casting.h"
33 auto barg = mlir::dyn_cast<mlir::BlockArgument>(v);
39 mlir::dyn_cast<mlir::acc::ComputeRegionOp>(block->
getParentOp());
42 assert(block == computeReg.getBody() &&
43 "block must be the body of acc.compute_region");
44 return computeReg.getOperand(barg);
52 return mlir::isa_and_nonnull<ACC_DATA_ENTRY_OPS>(def) ? def :
nullptr;
55template <
typename OpTy>
60 if (!region.
isAncestor(user->getParentRegion()))
62 return mlir::isa<OpTy>(user);
65 return llvm::all_of(val.
getUsers(), checkIfUsedOnlyByOpInside);
78std::optional<mlir::acc::ClauseDefaultValue>
80 std::optional<mlir::acc::ClauseDefaultValue> defaultAttr;
87 while (!defaultAttr.has_value() && currOp) {
90 std::optional<mlir::acc::ClauseDefaultValue>>(currOp)
91 .Case<ACC_COMPUTE_CONSTRUCT_OPS, mlir::acc::DataOp>(
92 [&](
auto op) {
return op.getDefaultAttr(); })
93 .Default([&](
Operation *) {
return std::nullopt; });
101 mlir::acc::VariableTypeCategory typeCategory =
102 mlir::acc::VariableTypeCategory::uncategorized;
103 if (
auto mappableTy = dyn_cast<mlir::acc::MappableType>(var.
getType()))
104 typeCategory = mappableTy.getTypeCategory(var);
105 else if (
auto pointerLikeTy =
106 dyn_cast<mlir::acc::PointerLikeType>(var.
getType()))
107 typeCategory = pointerLikeTy.getPointeeTypeCategory(
109 pointerLikeTy.getElementType());
114 return llvm::StringLiteral(
"<acc.varname.placeholder>");
124 return std::to_string(*constVal);
127 if (
auto varNameAttr = definingOp->getDiscardableAttrOfType<VarNameAttr>(
129 return varNameAttr.getName().str();
132 if (isa<ACC_DATA_ENTRY_OPS, MapInfoOp>(definingOp))
137 if (
auto addressOf = dyn_cast<AddressOfGlobalOpInterface>(definingOp))
138 return addressOf.getSymbol().getLeafReference().str();
141 if (
auto viewOp = dyn_cast<ViewLikeOpInterface>(definingOp)) {
142 current = viewOp.getViewSource();
154 assert(kind == mlir::acc::RecipeKind::private_recipe ||
155 kind == mlir::acc::RecipeKind::firstprivate_recipe ||
156 kind == mlir::acc::RecipeKind::reduction_recipe);
157 if (!llvm::isa<mlir::acc::PointerLikeType, mlir::acc::MappableType>(type))
160 std::string recipeName;
161 llvm::raw_string_ostream ss(recipeName);
162 ss << (kind == mlir::acc::RecipeKind::private_recipe ?
"privatization_"
163 : kind == mlir::acc::RecipeKind::firstprivate_recipe
164 ?
"firstprivatization_"
173 for (
char &c : recipeName) {
174 if (!std::isalnum(
static_cast<unsigned char>(c)) && c !=
'.' && c !=
'_') {
179 else if (c ==
'(' || c ==
')' || c ==
'[' || c ==
']' || c ==
'{' ||
180 c ==
'}' || c ==
'<' || c ==
'>')
191 if (
auto partialEntityAccessOp =
193 if (!partialEntityAccessOp.isCompleteView())
194 return partialEntityAccessOp.getBaseEntity();
204 mlir::SymbolRefAttr symbol) {
208 for (
auto gpuModule : moduleOp.getOps<mlir::gpu::GPUModuleOp>()) {
210 gpuModule, symbol.getRootReference()))
217 mlir::SymbolRefAttr symbol,
224 *definingOpPtr = gpuOp;
237 *definingOpPtr = definingOp;
242 if (mlir::isa<mlir::accomp::RecipeInterface>(definingOp))
248 mlir::dyn_cast<mlir::acc::GlobalVariableOpInterface>(definingOp))
249 if (globalVar.isDeviceAccessible())
254 mlir::dyn_cast_if_present<mlir::FunctionOpInterface>(definingOp)) {
268 func.getFunctionBody().empty() &&
func.getName().starts_with(
"llvm.") &&
269 llvm::Intrinsic::lookupIntrinsicID(
func.getName()) !=
270 llvm::Intrinsic::not_intrinsic)
283static mlir::acc::GlobalVariableOpInterface
291 mlir::acc::GlobalVariableOpInterface>(addrOf, symbol);
298 if (
auto mappableTy = dyn_cast<mlir::acc::MappableType>(val.
getType()))
299 if (mappableTy.isDeviceAccessible(val, symbolTables))
302 if (
auto pointerLikeTy = dyn_cast<mlir::acc::PointerLikeType>(val.
getType()))
303 if (pointerLikeTy.isDeviceAccessible(val, symbolTables))
312 if (
auto declareAttr =
315 if (declareAttr.getDataClause().getValue() ==
316 mlir::acc::DataClause::acc_deviceptr)
321 if (
auto partialAccess =
322 dyn_cast<mlir::acc::PartialEntityAccessOpInterface>(defOp)) {
323 if (
mlir::Value base = partialAccess.getBaseEntity())
328 if (
auto addrOfIface =
329 dyn_cast<mlir::acc::AddressOfGlobalOpInterface>(defOp)) {
330 auto symbol = addrOfIface.getSymbol();
332 return global.isDeviceAccessible();
342 if (
auto mappableTy = dyn_cast<mlir::acc::MappableType>(val.
getType()))
343 if (mappableTy.isInDeviceMemory(val, symbolTables))
346 if (
auto pointerLikeTy = dyn_cast<mlir::acc::PointerLikeType>(val.
getType()))
347 if (pointerLikeTy.isInDeviceMemory(val, symbolTables))
356 if (
auto declareAttr =
359 if (declareAttr.getDataClause().getValue() ==
360 mlir::acc::DataClause::acc_deviceptr)
365 if (
auto partialAccess =
366 dyn_cast<mlir::acc::PartialEntityAccessOpInterface>(defOp)) {
367 if (
mlir::Value base = partialAccess.getBaseEntity())
372 if (
auto addrOfIface =
373 dyn_cast<mlir::acc::AddressOfGlobalOpInterface>(defOp)) {
374 auto symbol = addrOfIface.getSymbol();
376 return global.isInDeviceMemory();
387 llvm::isa<mlir::VectorType>(type))
391 if (isa_and_nonnull<ACC_DATA_ENTRY_OPS>(val.
getDefiningOp()))
404 if (
auto func = parent->getParentOfType<mlir::FunctionOpInterface>()) {
407 llvm::is_contained(
func.getArguments(), val))
419 llvm::SmallSetVector<mlir::Value, 8> dominatingDataClauses;
422 .Case<mlir::acc::ParallelOp, mlir::acc::KernelsOp, mlir::acc::SerialOp>(
424 for (
auto dataClause : op.getDataClauseOperands()) {
425 dominatingDataClauses.insert(dataClause);
432 while (currParentOp) {
433 if (mlir::isa<mlir::acc::DataOp>(currParentOp)) {
434 for (
auto dataClause : mlir::dyn_cast<mlir::acc::DataOp>(currParentOp)
435 .getDataClauseOperands()) {
436 dominatingDataClauses.insert(dataClause);
439 currParentOp = currParentOp->getParentOp();
446 return dominatingDataClauses.takeVector();
451 funcOp->walk([&](mlir::acc::DeclareEnterOp declareEnterOp) {
452 if (domInfo.
dominates(declareEnterOp.getOperation(), computeConstructOp)) {
455 for (
auto *user : declareEnterOp.getToken().getUsers())
456 if (
auto declareExit = mlir::dyn_cast<mlir::acc::DeclareExitOp>(user))
457 exits.push_back(declareExit);
461 if (!exits.empty() &&
462 llvm::all_of(exits, [&](mlir::acc::DeclareExitOp exitOp) {
463 return postDomInfo.postDominates(exitOp, computeConstructOp);
465 for (
auto dataClause : declareEnterOp.getDataClauseOperands())
466 dominatingDataClauses.insert(dataClause);
471 return dominatingDataClauses.takeVector();
475 if (
auto ifOp = mlir::dyn_cast<mlir::scf::IfOp>(op))
476 return ifOp.getCondition();
478 if (!mlir::isa<mlir::RegionBranchOpInterface>(op) ||
479 mlir::isa<mlir::LoopLikeOpInterface>(op))
490 if (deviceTypes.empty())
496 mlir::FunctionOpInterface>(parent);
497 parent = parent->getParentOp()) {
503 auto onDeviceOp = condition.
getDefiningOp<mlir::acc::OnDeviceOp>();
507 std::optional<int64_t> deviceTypeValue =
509 if (!deviceTypeValue)
512 bool onTarget = llvm::is_contained(deviceTypes, *deviceTypeValue);
517 if ((!onTarget && inThen) || (onTarget && inElse))
525 const std::function<std::string()> &messageFn,
526 llvm::StringRef category) {
533 llvm::StringRef funcName;
534 if (
auto func = dyn_cast<mlir::FunctionOpInterface>(op))
535 funcName =
func.getName();
536 else if (
auto funcOp = op->
getParentOfType<mlir::FunctionOpInterface>())
537 funcName = funcOp.getName();
540 if (!funcName.empty())
541 opts = opts.function(funcName);
543 auto remark = engine->emitOptimizationRemark(loc, opts);
static bool isOnlyUsedByOpClauses(mlir::Value val, mlir::Region ®ion)
static mlir::Value getIfCondition(mlir::Operation *op)
static mlir::Operation * lookupSymbolInGPUModules(mlir::Operation *user, mlir::SymbolRefAttr symbol)
Look up symbol in the gpu.modules of the enclosing module.
static mlir::acc::GlobalVariableOpInterface lookupGlobalVariable(mlir::Operation *addrOf, mlir::SymbolRefAttr symbol, mlir::SymbolTableCollection *symbolTables)
Finds the global variable that addrOf refers to, through symbolTables when provided (cached lookup) a...
static std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
MLIRContext * getContext() const
Return the context this attribute belongs to.
Block represents an ordered list of Operations.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
A class for computing basic dominance information.
bool dominates(Operation *a, Operation *b) const
Return true if operation A dominates operation B, i.e.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
remark::detail::RemarkEngine * getRemarkEngine()
Returns the remark engine for this context, or nullptr if none has been set.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
unsigned getNumOperands()
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
bool hasDiscardableAttr(StringRef name)
Return true if this operation has a discardable attribute with the provided name.
AttrClass getDiscardableAttrOfType(StringRef name)
Access a discardable attribute by name and cast it to AttrClass.
Region * getParentRegion()
Returns the region to which the instruction belongs.
A class for computing basic postdominance information.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
bool isAncestor(Region *other)
Return true if this region is ancestor of the other region.
Operation * getParentOp()
Return the parent operation this region is attached to.
ParentT getParentOfType()
Find the first parent operation of the given type, or nullptr if there is no ancestor operation.
This class represents a collection of SymbolTables.
virtual Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
@ Private
The symbol is private and may only be referenced by SymbolRefAttrs local to the operations within the...
static Operation * lookupSymbolIn(Operation *op, StringAttr symbol)
Returns the operation registered with the given symbol name with the regions of 'symbolTableOp'.
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
void print(raw_ostream &os) const
Print the current type.
bool isSignlessInteger() const
Return true if this is a signless integer type (with the specified width).
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
user_range getUsers() const
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
#define ACC_COMPUTE_CONSTRUCT_OPS
mlir::acc::VariableTypeCategory getTypeCategory(mlir::Value var)
Get the type category of an OpenACC variable.
std::string getVariableName(mlir::Value v)
Attempts to extract the variable name from a value by walking through view-like operations until an a...
bool isValidSymbolUse(mlir::Operation *user, mlir::SymbolRefAttr symbol, mlir::Operation **definingOpPtr=nullptr)
Check if a symbol use is valid for use in an OpenACC region.
bool isAccRoutine(mlir::Operation *op)
Used to check whether the current operation is marked with acc routine.
static constexpr StringLiteral getSpecializedRoutineAttrName()
mlir::Value getACCOperandForBlockArg(mlir::Value v)
If v is not a block argument of an acc.compute_region body, returns nullptr.
mlir::Operation * getACCDataClauseOpForBlockArg(mlir::Value v)
If v is not a block argument of an acc.compute_region body, returns nullptr.
std::optional< ClauseDefaultValue > getDefaultAttr(mlir::Operation *op)
Looks for an OpenACC default attribute on the current operation op or in a parent operation which enc...
bool isSpecializedAccRoutine(mlir::Operation *op)
Used to check whether this is a specialized accelerator version of acc routine function.
bool isOnlyUsedByReductionClauses(mlir::Value val, mlir::Region ®ion)
Returns true if this value is only used by acc.reduction operations in the region.
std::optional< llvm::StringRef > getVarName(mlir::Operation *accOp)
Used to obtain the name from an acc operation.
static constexpr StringLiteral getRoutineInfoAttrName()
mlir::Operation * getEnclosingComputeOp(mlir::Region ®ion)
Used to obtain the enclosing compute construct operation that contains the provided region.
bool isValidValueUse(mlir::Value val, mlir::Region ®ion, mlir::SymbolTableCollection *symbolTables=nullptr)
Check if a value use is valid in an OpenACC region.
llvm::StringLiteral getVarNamePlaceholder()
Returns a placeholder string for use as an acc.var_name attribute value when the actual variable name...
llvm::SmallVector< mlir::Value > getDominatingDataClauses(mlir::Operation *computeConstructOp, mlir::DominanceInfo &domInfo, mlir::PostDominanceInfo &postDomInfo)
Collects all data clauses that dominate the compute construct.
bool isInDeviceMemoryValue(mlir::Value val, mlir::SymbolTableCollection *symbolTables=nullptr)
Check if a value is backed by memory that is residing in the current device, and therefore requires n...
static constexpr StringLiteral getVarNameAttrName()
std::string getRecipeName(mlir::acc::RecipeKind kind, mlir::Type type)
Get the recipe name for a given recipe kind and type.
remark::detail::InFlightRemark emitRemark(mlir::Operation *op, const std::function< std::string()> &messageFn, llvm::StringRef category="openacc")
Emit an OpenACC remark with lazy message generation.
static constexpr StringLiteral getDeclareAttrName()
Used to obtain the attribute name for declare.
bool isDeviceAccessibleValue(mlir::Value val, mlir::SymbolTableCollection *symbolTables=nullptr)
Check if a value represents device data.
bool isInOffTargetBranch(mlir::Operation *op, llvm::ArrayRef< int64_t > deviceTypes)
Returns true if op is in a branch that does not execute on the target.
mlir::Value getBaseEntity(mlir::Value val)
bool isOnlyUsedByPrivateClauses(mlir::Value val, mlir::Region ®ion)
Returns true if this value is only used by acc.private operations in the region.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.