188#include "llvm/ADT/DenseSet.h"
189#include "llvm/ADT/SmallVector.h"
190#include "llvm/ADT/TypeSwitch.h"
194#define GEN_PASS_DEF_ACCIMPLICITDECLARE
195#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
199#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;
386 void runOnOperation()
override {
387 ModuleOp mod = getOperation();
399 hoistNonConstantDirectUses(accOp, accSupport);
407 GlobalOpSetT globalsToAccDeclare;
408 std::optional<llvm::DenseSet<StringAttr>> usedSymbols =
409 collectUsedSymbolsExcludingRecipeSelfUses(mod);
414 collectGlobalsFromDeviceRegion(
415 accOp.getRegion(), globalsToAccDeclare, accSupport, symTab);
417 .Case([&](FunctionOpInterface
func) {
421 collectGlobalsFromDeviceRegion(
func.getFunctionBody(),
422 globalsToAccDeclare, accSupport,
425 .Case([&](acc::GlobalVariableOpInterface globalVarOp) {
427 if (
Region *initRegion = globalVarOp.getInitRegion())
428 collectGlobalsFromDeviceRegion(*initRegion, globalsToAccDeclare,
431 .Case([&](acc::PrivateRecipeOp privateRecipe) {
432 if (hasRelevantRecipeUse(privateRecipe, mod, usedSymbols)) {
433 collectGlobalsFromDeviceRegion(privateRecipe.getInitRegion(),
434 globalsToAccDeclare, accSupport,
436 collectGlobalsFromDeviceRegion(privateRecipe.getDestroyRegion(),
437 globalsToAccDeclare, accSupport,
441 .Case([&](acc::FirstprivateRecipeOp firstprivateRecipe) {
442 if (hasRelevantRecipeUse(firstprivateRecipe, mod, usedSymbols)) {
443 collectGlobalsFromDeviceRegion(firstprivateRecipe.getInitRegion(),
444 globalsToAccDeclare, accSupport,
446 collectGlobalsFromDeviceRegion(
447 firstprivateRecipe.getDestroyRegion(), globalsToAccDeclare,
449 collectGlobalsFromDeviceRegion(firstprivateRecipe.getCopyRegion(),
450 globalsToAccDeclare, accSupport,
454 .Case([&](acc::ReductionRecipeOp reductionRecipe) {
455 if (hasRelevantRecipeUse(reductionRecipe, mod, usedSymbols)) {
456 collectGlobalsFromDeviceRegion(reductionRecipe.getInitRegion(),
457 globalsToAccDeclare, accSupport,
459 collectGlobalsFromDeviceRegion(
460 reductionRecipe.getCombinerRegion(), globalsToAccDeclare,
468 for (
Operation *globalOp : globalsToAccDeclare) {
470 llvm::dbgs() <<
"Global is being `acc declare copyin`d: ";
471 globalOp->
print(llvm::dbgs(),
473 llvm::dbgs() <<
"\n");
476 addDeclareAttr(context, globalOp, acc::DataClause::acc_copyin);
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.
static constexpr StringLiteral getDeclareAttrName()
Used to obtain the attribute name for declare.
Include the generated interface declarations.
llvm::TypeSwitch< T, ResultT > TypeSwitch