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 <type_traits>
223#define GEN_PASS_DEF_ACCIMPLICITDATA
224#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
228#define DEBUG_TYPE "acc-implicit-data"
238 void runOnOperation()
override;
243 template <
typename OpT>
244 Operation *getOriginalDataClauseOpForAlias(
250 template <
typename OpT>
251 Operation *generateDataClauseOpForCandidate(
252 Value var, ModuleOp &module,
OpBuilder &builder, OpT computeConstructOp,
254 const std::optional<acc::ClauseDefaultValue> &defaultClause);
257 template <
typename OpT>
259 generateImplicitDataOps(ModuleOp &module, OpT computeConstructOp,
260 std::optional<acc::ClauseDefaultValue> &defaultClause,
264 acc::PrivateRecipeOp generatePrivateRecipe(ModuleOp &module,
Value var,
269 acc::FirstprivateRecipeOp
270 generateFirstprivateRecipe(ModuleOp &module,
Value var,
Location loc,
275 void generateRecipes(ModuleOp &module,
OpBuilder &builder,
282static bool isCandidateForImplicitData(
Value val,
Region &accRegion,
291 if (isa_and_nonnull<ACC_DATA_ENTRY_OPS>(val.
getDefiningOp()))
305template <
typename OpT>
306Operation *ACCImplicitData::getOriginalDataClauseOpForAlias(
309 auto &aliasAnalysis = this->getAnalysis<AliasAnalysis>();
310 for (
auto dataClause : dominatingDataClauses) {
311 if (
auto *dataClauseOp = dataClause.getDefiningOp()) {
313 if (isa<acc::CopyinOp, acc::CreateOp, acc::PresentOp, acc::NoCreateOp,
314 acc::DevicePtrOp>(dataClauseOp))
315 if (aliasAnalysis.alias(
acc::getVar(dataClauseOp), var).isMust()) {
316 LLVM_DEBUG(llvm::dbgs()
317 <<
"Using existing data clause:\n\t" << *dataClauseOp
318 <<
"\n\tas reference when processing var:\n\t" << var
328static void fillInBoundsForUnknownDimensions(
Operation *dataClauseOp,
339 if (
auto mappableTy = dyn_cast<acc::MappableType>(type)) {
340 if (mappableTy.hasUnknownDimensions()) {
343 if (std::is_same_v<
decltype(dataClauseOp), acc::DevicePtrOp>)
347 auto bounds = mappableTy.generateAccBounds(var, builder);
349 dataClauseOp.getBoundsMutable().assign(bounds);
356ACCImplicitData::generatePrivateRecipe(ModuleOp &module,
Value var,
360 std::string recipeName =
361 accSupport.
getRecipeName(acc::RecipeKind::private_recipe, type, var);
364 auto existingRecipe =
module.lookupSymbol<acc::PrivateRecipeOp>(recipeName);
366 return existingRecipe;
373 acc::PrivateRecipeOp::createAndPopulate(builder, loc, recipeName, var);
374 if (!recipe.has_value())
375 return accSupport.
emitNYI(loc,
"implicit private"),
nullptr;
376 return recipe.value();
379acc::FirstprivateRecipeOp
380ACCImplicitData::generateFirstprivateRecipe(ModuleOp &module,
Value var,
384 std::string recipeName =
385 accSupport.
getRecipeName(acc::RecipeKind::firstprivate_recipe, type, var);
388 auto existingRecipe =
389 module.lookupSymbol<acc::FirstprivateRecipeOp>(recipeName);
391 return existingRecipe;
397 auto recipe = acc::FirstprivateRecipeOp::createAndPopulate(builder, loc,
399 if (!recipe.has_value())
400 return accSupport.
emitNYI(loc,
"implicit firstprivate"),
nullptr;
401 return recipe.value();
404void ACCImplicitData::generateRecipes(ModuleOp &module,
OpBuilder &builder,
407 auto &accSupport = this->getAnalysis<acc::OpenACCSupport>();
408 for (
auto var : newOperands) {
411 auto recipe = generatePrivateRecipe(
414 privateOp.setRecipeAttr(
415 SymbolRefAttr::get(module->getContext(), recipe.getSymName()));
416 }
else if (
auto firstprivateOp = var.
getDefiningOp<acc::FirstprivateOp>()) {
417 auto recipe = generateFirstprivateRecipe(
420 firstprivateOp.setRecipeAttr(SymbolRefAttr::get(
421 module->getContext(), recipe.getSymName().str()));
437template <
typename OpT>
438Operation *ACCImplicitData::generateDataClauseOpForCandidate(
439 Value var, ModuleOp &module,
OpBuilder &builder, OpT computeConstructOp,
441 const std::optional<acc::ClauseDefaultValue> &defaultClause) {
442 auto &accSupport = this->getAnalysis<acc::OpenACCSupport>();
443 acc::VariableTypeCategory typeCategory =
444 acc::VariableTypeCategory::uncategorized;
445 if (
auto mappableTy = dyn_cast<acc::MappableType>(var.
getType())) {
446 typeCategory = mappableTy.getTypeCategory(var);
447 }
else if (
auto pointerLikeTy =
448 dyn_cast<acc::PointerLikeType>(var.
getType())) {
449 typeCategory = pointerLikeTy.getPointeeTypeCategory(
451 pointerLikeTy.getElementType());
455 acc::bitEnumContainsAny(typeCategory, acc::VariableTypeCategory::scalar);
456 bool isAnyAggregate = acc::bitEnumContainsAny(
457 typeCategory, acc::VariableTypeCategory::aggregate);
458 Location loc = computeConstructOp->getLoc();
462 LLVM_DEBUG(llvm::dbgs() <<
"Using deviceptr clause because variable is "
464 return acc::DevicePtrOp::create(builder, loc, var,
470 op = getOriginalDataClauseOpForAlias(var, builder, computeConstructOp,
471 dominatingDataClauses);
473 if (isa<acc::NoCreateOp>(op))
474 return acc::NoCreateOp::create(builder, loc, var,
479 if (isa<acc::DevicePtrOp>(op))
480 return acc::DevicePtrOp::create(builder, loc, var,
487 return acc::PresentOp::create(builder, loc, var,
494 if (enableImplicitReductionCopy &&
496 computeConstructOp->getRegion(0))) {
498 acc::CopyinOp::create(builder, loc, var,
501 copyinOp.setDataClause(acc::DataClause::acc_reduction);
502 return copyinOp.getOperation();
504 if constexpr (std::is_same_v<OpT, acc::KernelsOp>) {
507 acc::CopyinOp::create(builder, loc, var,
510 copyinOp.setDataClause(acc::DataClause::acc_copy);
511 return copyinOp.getOperation();
514 return acc::FirstprivateOp::create(builder, loc, var,
518 }
else if (isAnyAggregate) {
522 if (defaultClause.has_value() &&
523 defaultClause.value() == acc::ClauseDefaultValue::Present) {
524 newDataOp = acc::PresentOp::create(builder, loc, var,
531 acc::CopyinOp::create(builder, loc, var,
534 copyinOp.setDataClause(acc::DataClause::acc_copy);
535 newDataOp = copyinOp.getOperation();
544 LLVM_DEBUG(llvm::dbgs()
545 <<
"Unhandled case for implicit data mapping " << var <<
"\n");
560static void legalizeValuesInRegion(
Region &accRegion,
563 for (
Value dataClause :
564 llvm::concat<Value>(newDataClauseOperands, newPrivateOperands)) {
571template <
typename OpT>
572static void addNewPrivateOperands(OpT &accOp,
574 if (privateOperands.empty())
577 for (
auto priv : privateOperands) {
578 if (isa<acc::PrivateOp>(priv.getDefiningOp())) {
579 accOp.getPrivateOperandsMutable().append(priv);
580 }
else if (isa<acc::FirstprivateOp>(priv.getDefiningOp())) {
581 accOp.getFirstprivateOperandsMutable().append(priv);
583 llvm_unreachable(
"unhandled reduction operand");
590 for (
auto *user : res.getUsers())
591 if (isa<ACC_DATA_EXIT_OPS>(user))
609 Value lastDataClause =
nullptr;
610 for (
auto dataEntry : llvm::reverse(sortedDataClauseOperands)) {
611 if (llvm::find(newDataClauseOperands, dataEntry) ==
612 newDataClauseOperands.end()) {
615 lastDataClause = dataEntry;
619 if (
auto *dataExitOp = findDataExitOp(lastDataClause.
getDefiningOp()))
621 Operation *dataEntryOp = dataEntry.getDefiningOp();
622 if (isa<acc::CopyinOp>(dataEntryOp)) {
623 auto copyoutOp = acc::CopyoutOp::create(
627 copyoutOp.setDataClause(acc::DataClause::acc_copy);
628 }
else if (isa<acc::PresentOp, acc::NoCreateOp>(dataEntryOp)) {
629 auto deleteOp = acc::DeleteOp::create(
630 builder, dataEntryOp->
getLoc(), dataEntry,
634 }
else if (isa<acc::DevicePtrOp>(dataEntryOp)) {
637 llvm_unreachable(
"unhandled data exit");
639 lastDataClause = dataEntry;
650 baseRefs.push_back(val);
655 if (val != baseRefs.front())
656 baseRefs.insert(baseRefs.begin(), val);
660 if (
auto viewLikeOp = val.
getDefiningOp<ViewLikeOpInterface>()) {
661 val = viewLikeOp.getViewSource();
662 baseRefs.insert(baseRefs.begin(), val);
676 std::find_if(sortedDataClauseOperands.begin(),
677 sortedDataClauseOperands.end(), [&](
Value dataClauseVal) {
680 auto var = acc::getVar(dataClauseVal.getDefiningOp());
681 auto baseRefs = getBaseRefsChain(var);
687 return std::find(baseRefs.begin(), baseRefs.end(),
688 acc::getVar(newClause)) != baseRefs.end();
691 if (insertPos != sortedDataClauseOperands.end()) {
692 newClause->
moveBefore(insertPos->getDefiningOp());
693 sortedDataClauseOperands.insert(insertPos,
acc::getAccVar(newClause));
699template <
typename OpT>
700void ACCImplicitData::generateImplicitDataOps(
701 ModuleOp &module, OpT computeConstructOp,
702 std::optional<acc::ClauseDefaultValue> &defaultClause,
706 if (!ignoreDefaultNone && defaultClause.has_value() &&
707 defaultClause.value() == acc::ClauseDefaultValue::None)
709 assert(!defaultClause.has_value() ||
710 defaultClause.value() == acc::ClauseDefaultValue::Present ||
711 (ignoreDefaultNone &&
712 defaultClause.value() == acc::ClauseDefaultValue::None));
715 Region &accRegion = computeConstructOp->getRegion(0);
720 auto isCandidate{[&](
Value val) ->
bool {
721 return isCandidateForImplicitData(val, accRegion, accSupport);
723 auto candidateVars(llvm::filter_to_vector(liveInValues, isCandidate));
724 if (candidateVars.empty())
731 if (!candidateVars.empty()) {
732 LLVM_DEBUG(llvm::dbgs() <<
"== Generating clauses for ==\n"
733 << computeConstructOp <<
"\n");
735 auto &domInfo = this->getAnalysis<DominanceInfo>();
736 auto &postDomInfo = this->getAnalysis<PostDominanceInfo>();
737 auto dominatingDataClauses =
739 for (
auto var : candidateVars) {
740 auto newDataClauseOp = generateDataClauseOpForCandidate(
741 var, module, builder, computeConstructOp, dominatingDataClauses,
743 fillInBoundsForUnknownDimensions(newDataClauseOp, builder);
744 LLVM_DEBUG(llvm::dbgs() <<
"Generated data clause for " << var <<
":\n"
745 <<
"\t" << *newDataClauseOp <<
"\n");
746 if (isa_and_nonnull<acc::PrivateOp, acc::FirstprivateOp, acc::ReductionOp>(
749 }
else if (isa_and_nonnull<ACC_DATA_CLAUSE_OPS>(newDataClauseOp)) {
757 legalizeValuesInRegion(accRegion, newPrivateOperands, newDataClauseOperands);
761 if constexpr (!std::is_same_v<OpT, acc::KernelsOp>)
762 generateRecipes(module, builder, computeConstructOp, newPrivateOperands);
766 computeConstructOp.getDataClauseOperands());
767 for (
auto newClause : newDataClauseOperands)
768 insertInSortedOrder(sortedDataClauseOperands, newClause.getDefiningOp());
771 generateDataExitOperations(builder, computeConstructOp, newDataClauseOperands,
772 sortedDataClauseOperands);
774 if constexpr (!std::is_same_v<OpT, acc::KernelsOp>)
775 addNewPrivateOperands(computeConstructOp, newPrivateOperands);
776 computeConstructOp.getDataClauseOperandsMutable().assign(
777 sortedDataClauseOperands);
780void ACCImplicitData::runOnOperation() {
781 ModuleOp module = this->getOperation();
785 module.walk([&](Operation *op) {
786 if (isa<ACC_COMPUTE_CONSTRUCT_OPS>(op)) {
787 assert(op->getNumRegions() == 1 && "must have 1 region");
792 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 isDeviceValue(mlir::Value val)
Check if a value represents device data.
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