189#include "llvm/ADT/DenseSet.h"
190#include "llvm/ADT/SmallVector.h"
191#include "llvm/ADT/TypeSwitch.h"
195#define GEN_PASS_DEF_ACCIMPLICITDECLARE
196#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
200#define DEBUG_TYPE "acc-implicit-declare"
205using GlobalOpSetT = llvm::SmallSetVector<Operation *, 16>;
210static bool isGlobalUseCandidateForHoisting(
Operation *globalOp,
212 SymbolRefAttr symbol,
219 bool isInitializedConstant =
false;
220 bool isFunction =
false;
222 if (
auto globalVarOp = dyn_cast<acc::GlobalVariableOpInterface>(globalOp))
223 isInitializedConstant =
224 globalVarOp.isConstant() && globalVarOp.hasInitializer();
226 if (isa<FunctionOpInterface>(globalOp))
239 return !isInitializedConstant && !isFunction;
243bool isValidForAccDeclare(
Operation *globalOp) {
245 return !isa<FunctionOpInterface>(globalOp);
253static std::optional<llvm::DenseSet<StringAttr>>
254collectUsedSymbolsExcludingRecipeSelfUses(ModuleOp mod) {
257 std::optional<SymbolTable::UseRange> uses =
263 auto isRecipeSelfUse = [](
Operation *user, StringAttr name) {
264 if (
auto recipe = dyn_cast<acc::PrivateRecipeOp>(user))
265 return recipe.getNameAttr() == name;
266 if (
auto recipe = dyn_cast<acc::FirstprivateRecipeOp>(user))
267 return recipe.getNameAttr() == name;
268 if (
auto recipe = dyn_cast<acc::ReductionRecipeOp>(user))
269 return recipe.getNameAttr() == name;
273 StringAttr name = use.getSymbolRef().getLeafReference();
274 if (!isRecipeSelfUse(use.getUser(), name))
275 usedSymbols.insert(name);
284template <
typename RecipeOpT>
285static bool hasRelevantRecipeUse(
286 RecipeOpT &recipeOp, ModuleOp &mod,
288 auto recipeName = recipeOp.getNameAttr();
290 return usedSymbols->contains(recipeName);
292 std::optional<SymbolTable::UseRange> symbolUses = recipeOp.getSymbolUses(mod);
295 if (!symbolUses.has_value() || symbolUses->empty())
299 auto begin = symbolUses->begin();
300 auto end = symbolUses->end();
301 if (begin != end && std::next(begin) != end)
306 return use.
getUser() != recipeOp.getOperation();
312template <
typename AccConstructT>
313static void hoistNonConstantDirectUses(AccConstructT accOp,
315 accOp.
walk([&](acc::AddressOfGlobalOpInterface addrOfOp) {
316 SymbolRefAttr symRef = addrOfOp.getSymbol();
320 if (isGlobalUseCandidateForHoisting(globalOp, addrOfOp, symRef,
322 auto computeRegionParent =
323 addrOfOp->getParentOfType<acc::ComputeRegionOp>();
324 addrOfOp->moveBefore(accOp);
325 if (computeRegionParent)
326 for (
Value v : addrOfOp->getResults())
327 computeRegionParent.wireHoistedValueThroughIns(v);
329 llvm::dbgs() <<
"Hoisted:\n\t" << addrOfOp <<
"\n\tfrom:\n\t";
330 accOp->print(llvm::dbgs(),
332 llvm::dbgs() <<
"\n");
339static void collectGlobalsFromDeviceRegion(
Region ®ion,
340 GlobalOpSetT &globals,
345 auto addrOfOp = dyn_cast<acc::AddressOfGlobalOpInterface>(op);
347 SymbolRefAttr symRef = addrOfOp.getSymbol();
355 if (isCandidate && globalOp && isValidForAccDeclare(globalOp))
356 globals.insert(globalOp);
357 }
else if (
auto indirectAccessOp =
358 dyn_cast<acc::IndirectGlobalAccessOpInterface>(op)) {
361 indirectAccessOp.getReferencedSymbols(symbols, &symTab);
362 for (SymbolRefAttr symRef : symbols)
364 if (isValidForAccDeclare(globalOp))
365 globals.insert(globalOp);
372 acc::DataClause clause) {
375 acc::DeclareAttr::get(context,
376 acc::DataClauseAttr::get(context, clause)));
381class ACCImplicitDeclare
384 using ACCImplicitDeclareBase<ACCImplicitDeclare>::ACCImplicitDeclareBase;
387 ACCImplicitDeclareBase<ACCImplicitDeclare>::getDependentDialects(registry);
391 void runOnOperation()
override {
392 ModuleOp mod = getOperation();
404 hoistNonConstantDirectUses(accOp, accSupport);
412 GlobalOpSetT globalsToAccDeclare;
413 std::optional<llvm::DenseSet<StringAttr>> usedSymbols =
414 collectUsedSymbolsExcludingRecipeSelfUses(mod);
419 collectGlobalsFromDeviceRegion(
420 accOp.getRegion(), globalsToAccDeclare, accSupport, symTab);
422 .Case([&](FunctionOpInterface
func) {
426 collectGlobalsFromDeviceRegion(
func.getFunctionBody(),
427 globalsToAccDeclare, accSupport,
430 .Case([&](acc::GlobalVariableOpInterface globalVarOp) {
432 if (
Region *initRegion = globalVarOp.getInitRegion())
433 collectGlobalsFromDeviceRegion(*initRegion, globalsToAccDeclare,
436 .Case([&](acc::PrivateRecipeOp privateRecipe) {
437 if (hasRelevantRecipeUse(privateRecipe, mod, usedSymbols)) {
438 collectGlobalsFromDeviceRegion(privateRecipe.getInitRegion(),
439 globalsToAccDeclare, accSupport,
441 collectGlobalsFromDeviceRegion(privateRecipe.getDestroyRegion(),
442 globalsToAccDeclare, accSupport,
446 .Case([&](acc::FirstprivateRecipeOp firstprivateRecipe) {
447 if (hasRelevantRecipeUse(firstprivateRecipe, mod, usedSymbols)) {
448 collectGlobalsFromDeviceRegion(firstprivateRecipe.getInitRegion(),
449 globalsToAccDeclare, accSupport,
451 collectGlobalsFromDeviceRegion(
452 firstprivateRecipe.getDestroyRegion(), globalsToAccDeclare,
454 collectGlobalsFromDeviceRegion(firstprivateRecipe.getCopyRegion(),
455 globalsToAccDeclare, accSupport,
459 .Case([&](acc::ReductionRecipeOp reductionRecipe) {
460 if (hasRelevantRecipeUse(reductionRecipe, mod, usedSymbols)) {
461 collectGlobalsFromDeviceRegion(reductionRecipe.getInitRegion(),
462 globalsToAccDeclare, accSupport,
464 collectGlobalsFromDeviceRegion(
465 reductionRecipe.getCombinerRegion(), globalsToAccDeclare,
473 for (
Operation *globalOp : globalsToAccDeclare) {
475 llvm::dbgs() <<
"Global is being `acc declare copyin`d: ";
476 globalOp->
print(llvm::dbgs(),
478 llvm::dbgs() <<
"\n");
481 addDeclareAttr(context, globalOp, acc::DataClause::acc_copyin);
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
MLIRContext is the top-level object for a collection of MLIR operations.
Set of flags used to control the behavior of the various IR print methods (e.g.
OpPrintingFlags & skipRegions(bool skip=true)
Skip printing regions.
Operation is the basic unit of execution within MLIR.
void setDiscardableAttr(StringAttr name, Attribute value)
Set a discardable attribute by name.
void print(raw_ostream &os, const OpPrintingFlags &flags={})
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
RetT walk(FnT &&callback)
Walk all nested operations, blocks or regions (including this region), depending on the type of callb...
This class represents a specific symbol use.
Operation * getUser() const
Return the operation user of this symbol reference.
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Operation * lookup(StringRef name) const
Look up a symbol with the specified name, returning null if no such name exists.
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
static std::optional< UseRange > getSymbolUses(Operation *from)
Get an iterator range for all of the uses, for any symbol, that are nested within the given operation...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
bool isValidSymbolUse(Operation *user, SymbolRefAttr symbol, Operation **definingOpPtr=nullptr)
Check if a symbol use is valid for use in an OpenACC region.
#define ACC_COMPUTE_CONSTRUCT_OPS
bool isAccRoutine(mlir::Operation *op)
Used to check whether the current operation is marked with acc routine.
bool isSpecializedAccRoutine(mlir::Operation *op)
Used to check whether this is a specialized accelerator version of acc routine function.
void registerAllExtensions(DialectRegistry ®istry)
Register the standard OpenACC models for GPU, LLVM and MemRef IR.
static constexpr StringLiteral getDeclareAttrName()
Used to obtain the attribute name for declare.
Include the generated interface declarations.
llvm::TypeSwitch< T, ResultT > TypeSwitch