202#include "llvm/ADT/SmallVectorExtras.h"
216#include "llvm/ADT/STLExtras.h"
217#include "llvm/ADT/SmallVector.h"
218#include "llvm/ADT/TypeSwitch.h"
219#include "llvm/Support/ErrorHandling.h"
220#include "llvm/Support/raw_ostream.h"
222#include <type_traits>
226#define GEN_PASS_DEF_ACCIMPLICITDATA
227#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
231#define DEBUG_TYPE "acc-implicit-data"
246 void runOnOperation()
override;
251 template <
typename OpT>
252 Operation *getOriginalDataClauseOpForAlias(
258 template <
typename OpT>
259 Operation *generateDataClauseOpForCandidate(
260 Value var, ModuleOp &module,
OpBuilder &builder, OpT computeConstructOp,
262 const std::optional<acc::ClauseDefaultValue> &defaultClause);
265 template <
typename OpT>
267 generateImplicitDataOps(ModuleOp &module, OpT computeConstructOp,
268 std::optional<acc::ClauseDefaultValue> &defaultClause,
272 acc::PrivateRecipeOp generatePrivateRecipe(ModuleOp &module,
Value var,
277 acc::FirstprivateRecipeOp
278 generateFirstprivateRecipe(ModuleOp &module,
Value var,
Location loc,
283 void generateRecipes(ModuleOp &module,
OpBuilder &builder,
298static bool isCandidateForImplicitData(
Value val,
Region &accRegion,
308 if (isa_and_nonnull<ACC_DATA_ENTRY_OPS>(val.
getDefiningOp()))
322template <
typename OpT>
323Operation *ACCImplicitData::getOriginalDataClauseOpForAlias(
326 auto &aliasAnalysis = this->getAnalysis<AliasAnalysis>();
327 for (
auto dataClause : dominatingDataClauses) {
328 if (
auto *dataClauseOp = dataClause.getDefiningOp()) {
330 if (isa<acc::CopyinOp, acc::CreateOp, acc::PresentOp, acc::NoCreateOp,
331 acc::DevicePtrOp>(dataClauseOp))
332 if (aliasAnalysis.alias(
acc::getVar(dataClauseOp), var).isMust()) {
333 LLVM_DEBUG(llvm::dbgs()
334 <<
"Using existing data clause:\n\t" << *dataClauseOp
335 <<
"\n\tas reference when processing var:\n\t" << var
345static void fillInBoundsForUnknownDimensions(
Operation *dataClauseOp,
356 if (
auto mappableTy = dyn_cast<acc::MappableType>(type)) {
357 if (mappableTy.hasUnknownDimensions()) {
360 if (std::is_same_v<
decltype(dataClauseOp), acc::DevicePtrOp>)
364 auto bounds = mappableTy.generateAccBounds(var, builder);
366 dataClauseOp.getBoundsMutable().assign(bounds);
373ACCImplicitData::generatePrivateRecipe(ModuleOp &module,
Value var,
377 std::string recipeName =
378 accSupport.
getRecipeName(acc::RecipeKind::private_recipe, type, var);
381 auto existingRecipe =
module.lookupSymbol<acc::PrivateRecipeOp>(recipeName);
383 return existingRecipe;
390 acc::PrivateRecipeOp::createAndPopulate(builder, loc, recipeName, var);
391 if (!recipe.has_value())
392 return accSupport.
emitNYI(loc,
"implicit private"),
nullptr;
393 return recipe.value();
396acc::FirstprivateRecipeOp
397ACCImplicitData::generateFirstprivateRecipe(ModuleOp &module,
Value var,
401 std::string recipeName =
402 accSupport.
getRecipeName(acc::RecipeKind::firstprivate_recipe, type, var);
405 auto existingRecipe =
406 module.lookupSymbol<acc::FirstprivateRecipeOp>(recipeName);
408 return existingRecipe;
414 auto recipe = acc::FirstprivateRecipeOp::createAndPopulate(builder, loc,
416 if (!recipe.has_value())
417 return accSupport.
emitNYI(loc,
"implicit firstprivate"),
nullptr;
418 return recipe.value();
421void ACCImplicitData::generateRecipes(ModuleOp &module,
OpBuilder &builder,
424 auto &accSupport = this->getAnalysis<acc::OpenACCSupport>();
425 for (
auto var : newOperands) {
428 auto recipe = generatePrivateRecipe(
431 privateOp.setRecipeAttr(
432 SymbolRefAttr::get(module->getContext(), recipe.getSymName()));
433 }
else if (
auto firstprivateOp = var.
getDefiningOp<acc::FirstprivateOp>()) {
434 auto recipe = generateFirstprivateRecipe(
437 firstprivateOp.setRecipeAttr(SymbolRefAttr::get(
438 module->getContext(), recipe.getSymName().str()));
454template <
typename OpT>
455Operation *ACCImplicitData::generateDataClauseOpForCandidate(
456 Value var, ModuleOp &module,
OpBuilder &builder, OpT computeConstructOp,
458 const std::optional<acc::ClauseDefaultValue> &defaultClause) {
459 auto &accSupport = this->getAnalysis<acc::OpenACCSupport>();
460 acc::VariableTypeCategory typeCategory =
461 acc::VariableTypeCategory::uncategorized;
462 if (
auto mappableTy = dyn_cast<acc::MappableType>(var.
getType())) {
463 typeCategory = mappableTy.getTypeCategory(var);
464 }
else if (
auto pointerLikeTy =
465 dyn_cast<acc::PointerLikeType>(var.
getType())) {
466 typeCategory = pointerLikeTy.getPointeeTypeCategory(
468 pointerLikeTy.getElementType());
472 acc::bitEnumContainsAny(typeCategory, acc::VariableTypeCategory::scalar);
473 bool isAnyAggregate = acc::bitEnumContainsAny(
474 typeCategory, acc::VariableTypeCategory::aggregate);
475 Location loc = computeConstructOp->getLoc();
484 LLVM_DEBUG(llvm::dbgs() <<
"Using deviceptr clause because variable is "
485 "in device memory\n");
486 return acc::DevicePtrOp::create(builder, loc, var,
492 op = getOriginalDataClauseOpForAlias(var, builder, computeConstructOp,
493 dominatingDataClauses);
495 if (isa<acc::NoCreateOp>(op))
496 return acc::NoCreateOp::create(builder, loc, var,
501 if (isa<acc::DevicePtrOp>(op))
502 return acc::DevicePtrOp::create(builder, loc, var,
509 return acc::PresentOp::create(builder, loc, var,
516 if (enableImplicitReductionCopy &&
518 computeConstructOp->getRegion(0))) {
520 acc::CopyinOp::create(builder, loc, var,
523 copyinOp.setDataClause(acc::DataClause::acc_reduction);
524 return copyinOp.getOperation();
526 if constexpr (std::is_same_v<OpT, acc::KernelsOp>) {
529 acc::CopyinOp::create(builder, loc, var,
532 copyinOp.setDataClause(acc::DataClause::acc_copy);
533 return copyinOp.getOperation();
536 return acc::FirstprivateOp::create(builder, loc, var,
540 }
else if (isAnyAggregate) {
544 if (defaultClause.has_value() &&
545 defaultClause.value() == acc::ClauseDefaultValue::Present) {
546 newDataOp = acc::PresentOp::create(builder, loc, var,
553 acc::CopyinOp::create(builder, loc, var,
556 copyinOp.setDataClause(acc::DataClause::acc_copy);
557 newDataOp = copyinOp.getOperation();
566 LLVM_DEBUG(llvm::dbgs()
567 <<
"Unhandled case for implicit data mapping " << var <<
"\n");
582static void legalizeValuesInRegion(
Region &accRegion,
585 for (
Value dataClause :
586 llvm::concat<Value>(newDataClauseOperands, newPrivateOperands)) {
593template <
typename OpT>
594static void addNewPrivateOperands(OpT &accOp,
596 if (privateOperands.empty())
599 for (
auto priv : privateOperands) {
600 if (isa<acc::PrivateOp>(priv.getDefiningOp())) {
601 accOp.getPrivateOperandsMutable().append(priv);
602 }
else if (isa<acc::FirstprivateOp>(priv.getDefiningOp())) {
603 accOp.getFirstprivateOperandsMutable().append(priv);
605 llvm_unreachable(
"unhandled reduction operand");
612 for (
auto *user : res.getUsers())
613 if (isa<ACC_DATA_EXIT_OPS>(user))
631 Value lastDataClause =
nullptr;
632 for (
auto dataEntry : llvm::reverse(sortedDataClauseOperands)) {
633 if (llvm::find(newDataClauseOperands, dataEntry) ==
634 newDataClauseOperands.end()) {
637 lastDataClause = dataEntry;
641 if (
auto *dataExitOp = findDataExitOp(lastDataClause.
getDefiningOp()))
643 Operation *dataEntryOp = dataEntry.getDefiningOp();
644 if (isa<acc::CopyinOp>(dataEntryOp)) {
645 auto copyoutOp = acc::CopyoutOp::create(
649 copyoutOp.setDataClause(acc::DataClause::acc_copy);
650 }
else if (isa<acc::PresentOp, acc::NoCreateOp>(dataEntryOp)) {
651 auto deleteOp = acc::DeleteOp::create(
652 builder, dataEntryOp->
getLoc(), dataEntry,
656 }
else if (isa<acc::DevicePtrOp>(dataEntryOp)) {
659 llvm_unreachable(
"unhandled data exit");
661 lastDataClause = dataEntry;
672 baseRefs.push_back(val);
677 if (val != baseRefs.front())
678 baseRefs.insert(baseRefs.begin(), val);
682 if (
auto viewLikeOp = val.
getDefiningOp<ViewLikeOpInterface>()) {
683 val = viewLikeOp.getViewSource();
684 baseRefs.insert(baseRefs.begin(), val);
698 std::find_if(sortedDataClauseOperands.begin(),
699 sortedDataClauseOperands.end(), [&](
Value dataClauseVal) {
702 auto var = acc::getVar(dataClauseVal.getDefiningOp());
703 auto baseRefs = getBaseRefsChain(var);
709 return std::find(baseRefs.begin(), baseRefs.end(),
710 acc::getVar(newClause)) != baseRefs.end();
713 if (insertPos != sortedDataClauseOperands.end()) {
714 newClause->
moveBefore(insertPos->getDefiningOp());
715 sortedDataClauseOperands.insert(insertPos,
acc::getAccVar(newClause));
721template <
typename OpT>
722void ACCImplicitData::generateImplicitDataOps(
723 ModuleOp &module, OpT computeConstructOp,
724 std::optional<acc::ClauseDefaultValue> &defaultClause,
728 if (!ignoreDefaultNone && defaultClause.has_value() &&
729 defaultClause.value() == acc::ClauseDefaultValue::None)
731 assert(!defaultClause.has_value() ||
732 defaultClause.value() == acc::ClauseDefaultValue::Present ||
733 (ignoreDefaultNone &&
734 defaultClause.value() == acc::ClauseDefaultValue::None));
737 Region &accRegion = computeConstructOp->getRegion(0);
742 auto isCandidate{[&](
Value val) ->
bool {
743 return isCandidateForImplicitData(val, accRegion, accSupport, symbolTables);
745 auto candidateVars(llvm::filter_to_vector(liveInValues, isCandidate));
746 if (candidateVars.empty())
753 if (!candidateVars.empty()) {
754 LLVM_DEBUG(llvm::dbgs() <<
"== Generating clauses for ==\n"
755 << computeConstructOp <<
"\n");
757 auto &domInfo = this->getAnalysis<DominanceInfo>();
758 auto &postDomInfo = this->getAnalysis<PostDominanceInfo>();
759 auto dominatingDataClauses =
761 for (
auto var : candidateVars) {
762 auto newDataClauseOp = generateDataClauseOpForCandidate(
763 var, module, builder, computeConstructOp, dominatingDataClauses,
765 fillInBoundsForUnknownDimensions(newDataClauseOp, builder);
766 LLVM_DEBUG(llvm::dbgs() <<
"Generated data clause for " << var <<
":\n"
767 <<
"\t" << *newDataClauseOp <<
"\n");
768 if (isa_and_nonnull<acc::PrivateOp, acc::FirstprivateOp, acc::ReductionOp>(
771 }
else if (isa_and_nonnull<ACC_DATA_CLAUSE_OPS>(newDataClauseOp)) {
779 legalizeValuesInRegion(accRegion, newPrivateOperands, newDataClauseOperands);
783 if constexpr (!std::is_same_v<OpT, acc::KernelsOp>)
784 generateRecipes(module, builder, computeConstructOp, newPrivateOperands);
788 computeConstructOp.getDataClauseOperands());
789 for (
auto newClause : newDataClauseOperands)
790 insertInSortedOrder(sortedDataClauseOperands, newClause.getDefiningOp());
793 generateDataExitOperations(builder, computeConstructOp, newDataClauseOperands,
794 sortedDataClauseOperands);
796 if constexpr (!std::is_same_v<OpT, acc::KernelsOp>)
797 addNewPrivateOperands(computeConstructOp, newPrivateOperands);
798 computeConstructOp.getDataClauseOperandsMutable().assign(
799 sortedDataClauseOperands);
802void ACCImplicitData::runOnOperation() {
803 ModuleOp module = this->getOperation();
807 symbolTables = &localSymbolTables;
809 module.walk([&](Operation *op) {
810 if (isa<ACC_COMPUTE_CONSTRUCT_OPS>(op)) {
811 assert(op->getNumRegions() == 1 && "must have 1 region");
816 generateImplicitDataOps(module, op, defaultClause, accSupport);
821 symbolTables =
nullptr;
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
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 a collection of SymbolTables.
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.
std::string getRecipeName(RecipeKind kind, Type type, Value var)
Get the recipe name for a given type and value.
bool isValidValueUse(Value v, Region ®ion, SymbolTableCollection *symbolTables=nullptr)
Check if a value use is legal in an OpenACC region.
void getDependentDialects(::mlir::DialectRegistry ®istry) const override
Return the dialect that must be loaded in the context before this pass.
#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 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...
void registerAllExtensions(DialectRegistry ®istry)
Register the standard OpenACC models for GPU, LLVM and MemRef IR.
bool isDeviceAccessibleValue(mlir::Value val, mlir::SymbolTableCollection *symbolTables=nullptr)
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