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());
221 omp::DeclareTargetAttr declareTargetAttr =
222 declareTargetOp ? declareTargetOp.getDeclareTarget() :
nullptr;
224 if (!declareTargetAttr)
229 for (
auto &callee : calls[funcOp.getName()])
231 {callee.getKey(), declareTargetAttr.getDeviceType()});
235 for (
auto &callee : calls[kTargetRegionsSymName])
237 {callee.getKey(), omp::DeclareTargetDeviceType::nohost});
242 while (!worklist.empty()) {
243 std::pair<StringRef, omp::DeclareTargetDeviceType> workItem =
244 worklist.pop_back_val();
245 auto funcOp = modOp.lookupSymbol<FunctionOpInterface>(workItem.first);
246 assert(funcOp &&
"a work item must point to an existing function");
252 auto declareTargetOp = dyn_cast<omp::DeclareTargetInterface>(*funcOp);
253 omp::DeclareTargetAttr declareTargetAttr =
254 declareTargetOp ? declareTargetOp.getDeclareTarget() :
nullptr;
255 if (!declareTargetOp ||
256 (declareTargetAttr && !declareTargetAttr.getImplicit()))
259 omp::DeclareTargetDeviceType changedDeviceType;
260 if (declareTargetAttr) {
262 omp::DeclareTargetDeviceType currentDeviceType =
263 declareTargetAttr.getDeviceType();
267 if (currentDeviceType == omp::DeclareTargetDeviceType::any ||
268 currentDeviceType == workItem.second) {
274 changedDeviceType = omp::DeclareTargetDeviceType::any;
280 if (funcOp.isPublic() || funcOp.isExternal())
281 changedDeviceType = omp::DeclareTargetDeviceType::any;
283 changedDeviceType = workItem.second;
287 declareTargetOp.setDeclareTarget(changedDeviceType,
288 omp::DeclareTargetCaptureClause::to,
291 for (
auto &callee : calls[workItem.first])
292 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.