188#include "llvm/ADT/SmallVector.h"
189#include "llvm/ADT/TypeSwitch.h"
193#define GEN_PASS_DEF_ACCIMPLICITDECLARE
194#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
198#define DEBUG_TYPE "acc-implicit-declare"
204using GlobalOpSetT = llvm::SmallSetVector<Operation *, 16>;
209static bool isGlobalUseCandidateForHoisting(
Operation *globalOp,
211 SymbolRefAttr symbol,
218 bool isInitializedConstant =
false;
219 bool isFunction =
false;
221 if (
auto globalVarOp = dyn_cast<acc::GlobalVariableOpInterface>(globalOp))
222 isInitializedConstant =
223 globalVarOp.isConstant() && globalVarOp.hasInitializer();
225 if (isa<FunctionOpInterface>(globalOp))
238 return !isInitializedConstant && !isFunction;
242bool isValidForAccDeclare(
Operation *globalOp) {
244 return !isa<FunctionOpInterface>(globalOp);
251template <
typename RecipeOpT>
252static bool hasRelevantRecipeUse(RecipeOpT &recipeOp, ModuleOp &mod) {
253 std::optional<SymbolTable::UseRange> symbolUses = recipeOp.getSymbolUses(mod);
256 if (!symbolUses.has_value() || symbolUses->empty())
260 auto begin = symbolUses->begin();
261 auto end = symbolUses->end();
262 if (begin != end && std::next(begin) != end)
267 return use.
getUser() != recipeOp.getOperation();
273template <
typename AccConstructT>
274static void hoistNonConstantDirectUses(AccConstructT accOp,
276 accOp.
walk([&](acc::AddressOfGlobalOpInterface addrOfOp) {
277 SymbolRefAttr symRef = addrOfOp.getSymbol();
281 if (isGlobalUseCandidateForHoisting(globalOp, addrOfOp, symRef,
283 auto computeRegionParent =
284 addrOfOp->getParentOfType<acc::ComputeRegionOp>();
285 addrOfOp->moveBefore(accOp);
286 if (computeRegionParent)
287 for (
Value v : addrOfOp->getResults())
288 computeRegionParent.wireHoistedValueThroughIns(v);
290 llvm::dbgs() <<
"Hoisted:\n\t" << addrOfOp <<
"\n\tfrom:\n\t";
291 accOp->print(llvm::dbgs(),
293 llvm::dbgs() <<
"\n");
300static void collectGlobalsFromDeviceRegion(
Region ®ion,
301 GlobalOpSetT &globals,
306 auto addrOfOp = dyn_cast<acc::AddressOfGlobalOpInterface>(op);
308 SymbolRefAttr symRef = addrOfOp.getSymbol();
316 if (isCandidate && globalOp && isValidForAccDeclare(globalOp))
317 globals.insert(globalOp);
318 }
else if (
auto indirectAccessOp =
319 dyn_cast<acc::IndirectGlobalAccessOpInterface>(op)) {
322 indirectAccessOp.getReferencedSymbols(symbols, &symTab);
323 for (SymbolRefAttr symRef : symbols)
325 if (isValidForAccDeclare(globalOp))
326 globals.insert(globalOp);
333 acc::DataClause clause) {
335 acc::DeclareAttr::get(context,
336 acc::DataClauseAttr::get(context, clause)));
341class ACCImplicitDeclare
342 :
public acc::impl::ACCImplicitDeclareBase<ACCImplicitDeclare> {
344 using ACCImplicitDeclareBase<ACCImplicitDeclare>::ACCImplicitDeclareBase;
346 void runOnOperation()
override {
347 ModuleOp mod = getOperation();
359 hoistNonConstantDirectUses(accOp, accSupport);
367 GlobalOpSetT globalsToAccDeclare;
372 collectGlobalsFromDeviceRegion(
373 accOp.getRegion(), globalsToAccDeclare, accSupport, symTab);
375 .Case([&](FunctionOpInterface
func) {
379 collectGlobalsFromDeviceRegion(
func.getFunctionBody(),
380 globalsToAccDeclare, accSupport,
383 .Case([&](acc::GlobalVariableOpInterface globalVarOp) {
385 if (
Region *initRegion = globalVarOp.getInitRegion())
386 collectGlobalsFromDeviceRegion(*initRegion, globalsToAccDeclare,
389 .Case([&](acc::PrivateRecipeOp privateRecipe) {
390 if (hasRelevantRecipeUse(privateRecipe, mod)) {
391 collectGlobalsFromDeviceRegion(privateRecipe.getInitRegion(),
392 globalsToAccDeclare, accSupport,
394 collectGlobalsFromDeviceRegion(privateRecipe.getDestroyRegion(),
395 globalsToAccDeclare, accSupport,
399 .Case([&](acc::FirstprivateRecipeOp firstprivateRecipe) {
400 if (hasRelevantRecipeUse(firstprivateRecipe, mod)) {
401 collectGlobalsFromDeviceRegion(firstprivateRecipe.getInitRegion(),
402 globalsToAccDeclare, accSupport,
404 collectGlobalsFromDeviceRegion(
405 firstprivateRecipe.getDestroyRegion(), globalsToAccDeclare,
407 collectGlobalsFromDeviceRegion(firstprivateRecipe.getCopyRegion(),
408 globalsToAccDeclare, accSupport,
412 .Case([&](acc::ReductionRecipeOp reductionRecipe) {
413 if (hasRelevantRecipeUse(reductionRecipe, mod)) {
414 collectGlobalsFromDeviceRegion(reductionRecipe.getInitRegion(),
415 globalsToAccDeclare, accSupport,
417 collectGlobalsFromDeviceRegion(
418 reductionRecipe.getCombinerRegion(), globalsToAccDeclare,
426 for (
Operation *globalOp : globalsToAccDeclare) {
428 llvm::dbgs() <<
"Global is being `acc declare copyin`d: ";
429 globalOp->
print(llvm::dbgs(),
431 llvm::dbgs() <<
"\n");
434 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 setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
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,...
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