19#include "llvm/ADT/StringMap.h"
20#include "llvm/ADT/StringSet.h"
21#include "llvm/ADT/TypeSwitch.h"
26#define GEN_PASS_DEF_MARKDECLARETARGETPASS
27#include "mlir/Dialect/OpenMP/Transforms/Passes.h.inc"
48 op.
walk([&](CallOpInterface callOp) {
50 if (
auto callableSymRef = dyn_cast<SymbolRefAttr>(callable)) {
51 StringRef callee = callableSymRef.getLeafReference();
53 targetCallees->insert(callee);
55 callees.insert(callee);
67 if (
auto symbolRefAttr = dyn_cast<SymbolRefAttr>(attr))
68 syms.insert(symbolRefAttr.getLeafReference());
84 inTarget ? targetRecipeUses : nestedRecipeUses;
86 inTarget ? targetFunctionUses : nestedFunctionUses;
91 .Case([&](omp::DistributeOp op) {
94 .Case([&](omp::LoopOp op) {
98 .Case([&](omp::MapInfoOp op) {
100 recipeUses.insert(mapperAttr.getValue());
102 .Case([&](omp::ParallelOp op) {
106 .Case([&](omp::ScopeOp op) {
110 .Case([&](omp::SectionsOp op) {
114 .Case([&](omp::SimdOp op) {
118 .Case([&](omp::SingleOp op) {
124 .Case([&](omp::TargetOp op) {
132 .Case([&](omp::TaskgroupOp op) {
135 .Case([&](omp::TaskloopContextOp op) {
140 .Case([&](omp::TaskOp op) {
144 .Case([&](omp::TeamsOp op) {
148 .Case([&](omp::WsloopOp op) {
157class MarkDeclareTargetPass
165 void runOnOperation()
override {
168 constexpr const static ::llvm::StringLiteral kTargetRegionsSymName =
171 ModuleOp modOp = getOperation();
176 llvm::StringMap<llvm::StringSet<>> calls;
177 for (
auto recipeOp : modOp.getOps<accomp::RecipeInterface>()) {
178 StringAttr recipeSymName;
179 if (
auto symOp = dyn_cast<SymbolOpInterface>(*recipeOp))
180 recipeSymName = symOp.getNameAttr();
181 else if (
auto privateOp = dyn_cast<omp::PrivateClauseOp>(*recipeOp))
182 recipeSymName = privateOp.getSymNameAttr();
187 calls[recipeSymName] = recipeCalls;
192 for (
auto funcOp : modOp.getOps<FunctionOpInterface>()) {
200 functionCalls, targetCalls);
201 for (
auto &recipe : recipeUses) {
203 functionCalls.insert_range(recipeCalls);
205 for (
auto &recipe : targetRecipeUses) {
207 targetCalls.insert_range(recipeCalls);
210 calls[funcOp.getName()] = functionCalls;
211 calls[kTargetRegionsSymName].insert_range(targetCalls);
218 for (
auto funcOp :
getOperation().getOps<FunctionOpInterface>()) {
219 auto declareTargetOp =
220 llvm::dyn_cast<omp::DeclareTargetInterface>(funcOp.getOperation());
222 if (!declareTargetOp || !declareTargetOp.isDeclareTarget())
227 for (
auto &callee : calls[funcOp.getName()])
229 {callee.getKey(), declareTargetOp.getDeclareTargetDeviceType()});
233 for (
auto &callee : calls[kTargetRegionsSymName])
235 {callee.getKey(), omp::DeclareTargetDeviceType::nohost});
240 while (!worklist.empty()) {
241 std::pair<StringRef, omp::DeclareTargetDeviceType> workItem =
242 worklist.pop_back_val();
243 auto funcOp = modOp.lookupSymbol<FunctionOpInterface>(workItem.first);
244 assert(funcOp &&
"a work item must point to an existing function");
250 auto declareTargetOp = dyn_cast<omp::DeclareTargetInterface>(*funcOp);
251 if (!declareTargetOp || (declareTargetOp.isDeclareTarget() &&
252 !declareTargetOp.isImplicitDeclareTarget()))
255 omp::DeclareTargetDeviceType changedDeviceType;
256 if (declareTargetOp.isDeclareTarget()) {
258 omp::DeclareTargetDeviceType currentDeviceType =
259 declareTargetOp.getDeclareTargetDeviceType();
263 if (currentDeviceType == omp::DeclareTargetDeviceType::any ||
264 currentDeviceType == workItem.second) {
270 changedDeviceType = omp::DeclareTargetDeviceType::any;
276 if (funcOp.isPublic() || funcOp.isExternal())
277 changedDeviceType = omp::DeclareTargetDeviceType::any;
279 changedDeviceType = workItem.second;
283 declareTargetOp.setDeclareTarget(changedDeviceType,
284 omp::DeclareTargetCaptureClause::to,
287 for (
auto &callee : calls[workItem.first])
288 worklist.push_back({callee.getKey(), changedDeviceType});
static void gatherNestedCallees(Operation &op, llvm::StringSet<> &callees, llvm::StringSet<> *targetCallees=nullptr)
Add to callees all names of the functions called from regions owned by op.
static void gatherNestedSymbolUses(Operation &op, llvm::StringSet<> &nestedRecipeUses, llvm::StringSet<> &targetRecipeUses, llvm::StringSet<> &nestedFunctionUses, llvm::StringSet<> &targetFunctionUses)
Go through all OpenMP dialect operations located in regions owned by op looking for symbol references...
static bool isInTargetRegion(Operation &op)
Check whether the given operation is located inside of an omp.target.
static void gatherSymsFromAttr(ArrayAttr arrayAttr, llvm::StringSet<> &syms)
Extract from arrayAttr and into syms the list of symbol names stored in the attribute.
Attributes are known-constant values of operations.
A symbol reference with a reference path containing a single element.
Operation is the basic unit of execution within MLIR.
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
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),...
Include the generated interface declarations.
A callable is either a symbol, or an SSA value, that is referenced by a call-like operation.