201#include "llvm/ADT/SmallVectorExtras.h"
215#include "llvm/ADT/STLExtras.h"
216#include "llvm/ADT/SmallVector.h"
217#include "llvm/ADT/TypeSwitch.h"
218#include "llvm/Support/ErrorHandling.h"
219#include "llvm/Support/raw_ostream.h"
221#include <type_traits>
225#define GEN_PASS_DEF_ACCIMPLICITDATA
226#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
230#define DEBUG_TYPE "acc-implicit-data"
240 void runOnOperation()
override;
245 template <
typename OpT>
246 Operation *getOriginalDataClauseOpForAlias(
252 template <
typename OpT>
253 Operation *generateDataClauseOpForCandidate(
254 Value var, ModuleOp &module,
OpBuilder &builder, OpT computeConstructOp,
256 const std::optional<acc::ClauseDefaultValue> &defaultClause);
259 template <
typename OpT>
261 generateImplicitDataOps(ModuleOp &module, OpT computeConstructOp,
262 std::optional<acc::ClauseDefaultValue> &defaultClause,
266 acc::PrivateRecipeOp generatePrivateRecipe(ModuleOp &module,
Value var,
271 acc::FirstprivateRecipeOp
272 generateFirstprivateRecipe(ModuleOp &module,
Value var,
Location loc,
277 void generateRecipes(ModuleOp &module,
OpBuilder &builder,
284static bool isCandidateForImplicitData(
Value val,
Region &accRegion,
293 if (isa_and_nonnull<ACC_DATA_ENTRY_OPS>(val.
getDefiningOp()))
307template <
typename OpT>
308Operation *ACCImplicitData::getOriginalDataClauseOpForAlias(
311 auto &aliasAnalysis = this->getAnalysis<AliasAnalysis>();
312 for (
auto dataClause : dominatingDataClauses) {
313 if (
auto *dataClauseOp = dataClause.getDefiningOp()) {
315 if (isa<acc::CopyinOp, acc::CreateOp, acc::PresentOp, acc::NoCreateOp,
316 acc::DevicePtrOp>(dataClauseOp))
317 if (aliasAnalysis.alias(
acc::getVar(dataClauseOp), var).isMust()) {
318 LLVM_DEBUG(llvm::dbgs()
319 <<
"Using existing data clause:\n\t" << *dataClauseOp
320 <<
"\n\tas reference when processing var:\n\t" << var
330static void fillInBoundsForUnknownDimensions(
Operation *dataClauseOp,
341 if (
auto mappableTy = dyn_cast<acc::MappableType>(type)) {
342 if (mappableTy.hasUnknownDimensions()) {
345 if (std::is_same_v<
decltype(dataClauseOp), acc::DevicePtrOp>)
349 auto bounds = mappableTy.generateAccBounds(var, builder);
351 dataClauseOp.getBoundsMutable().assign(bounds);
358ACCImplicitData::generatePrivateRecipe(ModuleOp &module,
Value var,
362 std::string recipeName =
363 accSupport.
getRecipeName(acc::RecipeKind::private_recipe, type, var);
366 auto existingRecipe =
module.lookupSymbol<acc::PrivateRecipeOp>(recipeName);
368 return existingRecipe;
375 acc::PrivateRecipeOp::createAndPopulate(builder, loc, recipeName, var);
376 if (!recipe.has_value())
377 return accSupport.
emitNYI(loc,
"implicit private"),
nullptr;
378 return recipe.value();
381acc::FirstprivateRecipeOp
382ACCImplicitData::generateFirstprivateRecipe(ModuleOp &module,
Value var,
386 std::string recipeName =
387 accSupport.
getRecipeName(acc::RecipeKind::firstprivate_recipe, type, var);
390 auto existingRecipe =
391 module.lookupSymbol<acc::FirstprivateRecipeOp>(recipeName);
393 return existingRecipe;
399 auto recipe = acc::FirstprivateRecipeOp::createAndPopulate(builder, loc,
401 if (!recipe.has_value())
402 return accSupport.
emitNYI(loc,
"implicit firstprivate"),
nullptr;
403 return recipe.value();
406void ACCImplicitData::generateRecipes(ModuleOp &module,
OpBuilder &builder,
409 auto &accSupport = this->getAnalysis<acc::OpenACCSupport>();
410 for (
auto var : newOperands) {
413 auto recipe = generatePrivateRecipe(
416 privateOp.setRecipeAttr(
417 SymbolRefAttr::get(module->getContext(), recipe.getSymName()));
418 }
else if (
auto firstprivateOp = var.
getDefiningOp<acc::FirstprivateOp>()) {
419 auto recipe = generateFirstprivateRecipe(
422 firstprivateOp.setRecipeAttr(SymbolRefAttr::get(
423 module->getContext(), recipe.getSymName().str()));
439template <
typename OpT>
440Operation *ACCImplicitData::generateDataClauseOpForCandidate(
441 Value var, ModuleOp &module,
OpBuilder &builder, OpT computeConstructOp,
443 const std::optional<acc::ClauseDefaultValue> &defaultClause) {
444 auto &accSupport = this->getAnalysis<acc::OpenACCSupport>();
445 acc::VariableTypeCategory typeCategory =
446 acc::VariableTypeCategory::uncategorized;
447 if (
auto mappableTy = dyn_cast<acc::MappableType>(var.
getType())) {
448 typeCategory = mappableTy.getTypeCategory(var);
449 }
else if (
auto pointerLikeTy =
450 dyn_cast<acc::PointerLikeType>(var.
getType())) {
451 typeCategory = pointerLikeTy.getPointeeTypeCategory(
453 pointerLikeTy.getElementType());
457 acc::bitEnumContainsAny(typeCategory, acc::VariableTypeCategory::scalar);
458 bool isAnyAggregate = acc::bitEnumContainsAny(
459 typeCategory, acc::VariableTypeCategory::aggregate);
460 Location loc = computeConstructOp->getLoc();
469 LLVM_DEBUG(llvm::dbgs() <<
"Using deviceptr clause because variable is "
470 "in device memory\n");
471 return acc::DevicePtrOp::create(builder, loc, var,
477 op = getOriginalDataClauseOpForAlias(var, builder, computeConstructOp,
478 dominatingDataClauses);
480 if (isa<acc::NoCreateOp>(op))
481 return acc::NoCreateOp::create(builder, loc, var,
486 if (isa<acc::DevicePtrOp>(op))
487 return acc::DevicePtrOp::create(builder, loc, var,
494 return acc::PresentOp::create(builder, loc, var,
501 if (enableImplicitReductionCopy &&
503 computeConstructOp->getRegion(0))) {
505 acc::CopyinOp::create(builder, loc, var,
508 copyinOp.setDataClause(acc::DataClause::acc_reduction);
509 return copyinOp.getOperation();
511 if constexpr (std::is_same_v<OpT, acc::KernelsOp>) {
514 acc::CopyinOp::create(builder, loc, var,
517 copyinOp.setDataClause(acc::DataClause::acc_copy);
518 return copyinOp.getOperation();
521 return acc::FirstprivateOp::create(builder, loc, var,
525 }
else if (isAnyAggregate) {
529 if (defaultClause.has_value() &&
530 defaultClause.value() == acc::ClauseDefaultValue::Present) {
531 newDataOp = acc::PresentOp::create(builder, loc, var,
538 acc::CopyinOp::create(builder, loc, var,
541 copyinOp.setDataClause(acc::DataClause::acc_copy);
542 newDataOp = copyinOp.getOperation();
551 LLVM_DEBUG(llvm::dbgs()
552 <<
"Unhandled case for implicit data mapping " << var <<
"\n");
567static void legalizeValuesInRegion(
Region &accRegion,
570 for (
Value dataClause :
571 llvm::concat<Value>(newDataClauseOperands, newPrivateOperands)) {
578template <
typename OpT>
579static void addNewPrivateOperands(OpT &accOp,
581 if (privateOperands.empty())
584 for (
auto priv : privateOperands) {
585 if (isa<acc::PrivateOp>(priv.getDefiningOp())) {
586 accOp.getPrivateOperandsMutable().append(priv);
587 }
else if (isa<acc::FirstprivateOp>(priv.getDefiningOp())) {
588 accOp.getFirstprivateOperandsMutable().append(priv);
590 llvm_unreachable(
"unhandled reduction operand");
597 for (
auto *user : res.getUsers())
598 if (isa<ACC_DATA_EXIT_OPS>(user))
616 Value lastDataClause =
nullptr;
617 for (
auto dataEntry : llvm::reverse(sortedDataClauseOperands)) {
618 if (llvm::find(newDataClauseOperands, dataEntry) ==
619 newDataClauseOperands.end()) {
622 lastDataClause = dataEntry;
626 if (
auto *dataExitOp = findDataExitOp(lastDataClause.
getDefiningOp()))
628 Operation *dataEntryOp = dataEntry.getDefiningOp();
629 if (isa<acc::CopyinOp>(dataEntryOp)) {
630 auto copyoutOp = acc::CopyoutOp::create(
634 copyoutOp.setDataClause(acc::DataClause::acc_copy);
635 }
else if (isa<acc::PresentOp, acc::NoCreateOp>(dataEntryOp)) {
636 auto deleteOp = acc::DeleteOp::create(
637 builder, dataEntryOp->
getLoc(), dataEntry,
641 }
else if (isa<acc::DevicePtrOp>(dataEntryOp)) {
644 llvm_unreachable(
"unhandled data exit");
646 lastDataClause = dataEntry;
657 baseRefs.push_back(val);
662 if (val != baseRefs.front())
663 baseRefs.insert(baseRefs.begin(), val);
667 if (
auto viewLikeOp = val.
getDefiningOp<ViewLikeOpInterface>()) {
668 val = viewLikeOp.getViewSource();
669 baseRefs.insert(baseRefs.begin(), val);
683 std::find_if(sortedDataClauseOperands.begin(),
684 sortedDataClauseOperands.end(), [&](
Value dataClauseVal) {
687 auto var = acc::getVar(dataClauseVal.getDefiningOp());
688 auto baseRefs = getBaseRefsChain(var);
694 return std::find(baseRefs.begin(), baseRefs.end(),
695 acc::getVar(newClause)) != baseRefs.end();
698 if (insertPos != sortedDataClauseOperands.end()) {
699 newClause->
moveBefore(insertPos->getDefiningOp());
700 sortedDataClauseOperands.insert(insertPos,
acc::getAccVar(newClause));
706template <
typename OpT>
707void ACCImplicitData::generateImplicitDataOps(
708 ModuleOp &module, OpT computeConstructOp,
709 std::optional<acc::ClauseDefaultValue> &defaultClause,
713 if (!ignoreDefaultNone && defaultClause.has_value() &&
714 defaultClause.value() == acc::ClauseDefaultValue::None)
716 assert(!defaultClause.has_value() ||
717 defaultClause.value() == acc::ClauseDefaultValue::Present ||
718 (ignoreDefaultNone &&
719 defaultClause.value() == acc::ClauseDefaultValue::None));
722 Region &accRegion = computeConstructOp->getRegion(0);
727 auto isCandidate{[&](
Value val) ->
bool {
728 return isCandidateForImplicitData(val, accRegion, accSupport);
730 auto candidateVars(llvm::filter_to_vector(liveInValues, isCandidate));
731 if (candidateVars.empty())
738 if (!candidateVars.empty()) {
739 LLVM_DEBUG(llvm::dbgs() <<
"== Generating clauses for ==\n"
740 << computeConstructOp <<
"\n");
742 auto &domInfo = this->getAnalysis<DominanceInfo>();
743 auto &postDomInfo = this->getAnalysis<PostDominanceInfo>();
744 auto dominatingDataClauses =
746 for (
auto var : candidateVars) {
747 auto newDataClauseOp = generateDataClauseOpForCandidate(
748 var, module, builder, computeConstructOp, dominatingDataClauses,
750 fillInBoundsForUnknownDimensions(newDataClauseOp, builder);
751 LLVM_DEBUG(llvm::dbgs() <<
"Generated data clause for " << var <<
":\n"
752 <<
"\t" << *newDataClauseOp <<
"\n");
753 if (isa_and_nonnull<acc::PrivateOp, acc::FirstprivateOp, acc::ReductionOp>(
756 }
else if (isa_and_nonnull<ACC_DATA_CLAUSE_OPS>(newDataClauseOp)) {
764 legalizeValuesInRegion(accRegion, newPrivateOperands, newDataClauseOperands);
768 if constexpr (!std::is_same_v<OpT, acc::KernelsOp>)
769 generateRecipes(module, builder, computeConstructOp, newPrivateOperands);
773 computeConstructOp.getDataClauseOperands());
774 for (
auto newClause : newDataClauseOperands)
775 insertInSortedOrder(sortedDataClauseOperands, newClause.getDefiningOp());
778 generateDataExitOperations(builder, computeConstructOp, newDataClauseOperands,
779 sortedDataClauseOperands);
781 if constexpr (!std::is_same_v<OpT, acc::KernelsOp>)
782 addNewPrivateOperands(computeConstructOp, newPrivateOperands);
783 computeConstructOp.getDataClauseOperandsMutable().assign(
784 sortedDataClauseOperands);
787void ACCImplicitData::runOnOperation() {
788 ModuleOp module = this->getOperation();
792 module.walk([&](Operation *op) {
793 if (isa<ACC_COMPUTE_CONSTRUCT_OPS>(op)) {
794 assert(op->getNumRegions() == 1 && "must have 1 region");
799 generateImplicitDataOps(module, op, defaultClause, accSupport);
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Operation is the basic unit of execution within MLIR.
void setDiscardableAttr(StringAttr name, Attribute value)
Set a discardable attribute by name.
Location getLoc()
The source location the operation was defined or derived from.
void moveBefore(Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
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.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
std::string getVariableName(Value v, VariableNameConfig config={})
Get the variable name for a given value.
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
bool isValidValueUse(Value v, Region ®ion)
Check if a value use is legal in an OpenACC region.
std::string getRecipeName(RecipeKind kind, Type type, Value var)
Get the recipe name for a given type and value.
#define ACC_COMPUTE_CONSTRUCT_OPS
#define ACC_DATA_ENTRY_OPS
#define ACC_DATA_EXIT_OPS
static constexpr StringLiteral getFromDefaultClauseAttrName()
mlir::Value getAccVar(mlir::Operation *accDataClauseOp)
Used to obtain the accVar from a data clause operation.
mlir::Value getVar(mlir::Operation *accDataClauseOp)
Used to obtain the var from a data clause operation.
std::optional< mlir::acc::DataClause > getDataClause(mlir::Operation *accDataEntryOp)
Used to obtain the dataClause from a data entry operation.
bool isPointerLikeType(mlir::Type type)
Used to check whether the provided type implements the PointerLikeType interface.
mlir::SmallVector< mlir::Value > getBounds(mlir::Operation *accDataClauseOp)
Used to obtain bounds from an acc data clause operation.
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 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.
llvm::SmallVector< mlir::Value > getDominatingDataClauses(mlir::Operation *computeConstructOp, mlir::DominanceInfo &domInfo, mlir::PostDominanceInfo &postDomInfo)
Collects all data clauses that dominate the compute construct.
bool isMappableType(mlir::Type type)
Used to check whether the provided type implements the MappableType interface.
bool isDeviceAccessibleValue(mlir::Value val)
Check if a value represents device data.
bool isInDeviceMemoryValue(mlir::Value val)
Check if a value is backed by memory that is residing in the current device, and therefore requires n...
mlir::Value getBaseEntity(mlir::Value val)
Include the generated interface declarations.
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region ®ion)
Replace all uses of orig within the given region with replacement.
llvm::SetVector< T, Vector, Set, N > SetVector
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.
void getUsedValuesDefinedAbove(Region ®ion, Region &limit, SetVector< Value > &values)
Fill values with a list of values defined at the ancestors of the limit region and used within region...
llvm::TypeSwitch< T, ResultT > TypeSwitch