MLIR 24.0.0git
ACCBindRoutine.cpp
Go to the documentation of this file.
1//===- ACCBindRoutine.cpp - OpenACC bind routine transform ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// The OpenACC `routine` directive may specify a `bind(name)` clause to
10// associate the routine with a different symbol for device code. This pass
11// finds calls inside offload regions that target such routines and rewrites the
12// callee to the bound symbol.
13//
14// Overview:
15// ---------
16// For the current function, walk call operations inside offload regions, or
17// gpu.func). If the callee is a function with an acc routine that has
18// bind(name), replace the call to use the bound symbol.
19//
20// Requirements:
21// -------------
22// - OffloadRegionOpInterface: the pass walks operations implementing this
23// interface to discover offload regions (e.g. acc.compute_region) and
24// rewrites calls inside their getOffloadRegion().
25// - CallOpInterface with working setCalleeFromCallable: call operations
26// must implement CallOpInterface and setCalleeFromCallable so the pass
27// can rewrite the callee to the symbol without invalidating the call.
28//
29//===----------------------------------------------------------------------===//
30
33
38#include "mlir/IR/SymbolTable.h"
40#include "llvm/Support/Debug.h"
41
42namespace mlir {
43namespace acc {
44#define GEN_PASS_DEF_ACCBINDROUTINE
45#define GEN_PASS_DEF_ACCMATERIALIZEROUTINEBINDTARGETS
46#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
47} // namespace acc
48} // namespace mlir
49
50#define DEBUG_TYPE "acc-bind-routine"
51
52using namespace mlir;
53using namespace mlir::acc;
54
55namespace {
56static RoutineOp getFirstAccRoutineOp(FunctionOpInterface funcOp,
57 const SymbolTable &fallbackSymTab) {
58 SymbolRefAttr routineRef;
60 auto attr = funcOp->getDiscardableAttrOfType<SpecializedRoutineAttr>(
62 routineRef = attr.getRoutine();
63 } else {
64 auto routineInfo = funcOp->getDiscardableAttrOfType<RoutineInfoAttr>(
66 assert(routineInfo && "expected acc.routine_info for acc routine function");
67 auto accRoutines = routineInfo.getAccRoutines();
68 assert(!accRoutines.empty() && "expected at least one acc routine");
69 routineRef = accRoutines[0];
70 }
71
72 RoutineOp routine =
74 if (routine)
75 return routine;
76 return fallbackSymTab.lookup<RoutineOp>(routineRef.getLeafReference());
77}
79class ACCMaterializeRoutineBindTargets
81 ACCMaterializeRoutineBindTargets> {
83 ACCMaterializeRoutineBindTargets>;
84
85public:
87
88 void runOnOperation() override {
89 Operation *symbolTableOp = getOperation();
90 if (!symbolTableOp->hasTrait<OpTrait::SymbolTable>()) {
91 symbolTableOp->emitError(
92 "root operation must have the SymbolTable trait");
94 return;
95 }
96
97 ModuleOp mod = dyn_cast<ModuleOp>(symbolTableOp);
98 if (!mod)
99 mod = symbolTableOp->getParentOfType<ModuleOp>();
100 if (!mod) {
101 symbolTableOp->emitError(
102 "root operation must be a builtin.module or nested in one");
104 return;
105 }
106
107 SymbolTable outerSymTab(mod);
108 SymbolTable insertSymTab(symbolTableOp);
109 Block *insertBlock = &symbolTableOp->getRegion(0).front();
110
111 symbolTableOp->walk<WalkOrder::PreOrder>([&](Operation *op) {
112 if (op != symbolTableOp && op->hasTrait<OpTrait::SymbolTable>())
114
115 auto callOp = dyn_cast<CallOpInterface>(op);
116 if (!callOp || !callOp.getCallableForCallee())
118 if (!callOp->getParentOfType<OffloadRegionOpInterface>() &&
119 !callOp->getParentOfType<gpu::GPUFuncOp>())
120 return WalkResult::advance();
121
122 auto calleeSymbolRef =
123 dyn_cast<SymbolRefAttr>(callOp.getCallableForCallee());
124 if (!calleeSymbolRef)
125 return WalkResult::advance();
126 FunctionOpInterface callee =
128 callOp, calleeSymbolRef);
129 if (!callee)
130 callee = outerSymTab.lookup<FunctionOpInterface>(
131 calleeSymbolRef.getLeafReference());
132 if (!callee || !(isAccRoutine(callee) || isSpecializedAccRoutine(callee)))
133 return WalkResult::advance();
134
135 if (auto routineInfo = callee->getDiscardableAttrOfType<RoutineInfoAttr>(
137 if (routineInfo.getAccRoutines().size() > 1)
138 return WalkResult::advance();
139
140 RoutineOp routine = getFirstAccRoutineOp(callee, outerSymTab);
141 if (!routine)
142 return WalkResult::advance();
143
144 auto bindNameOpt = routine.getEffectiveBindName(this->deviceType);
145 if (!bindNameOpt || !std::holds_alternative<StringAttr>(*bindNameOpt))
146 return WalkResult::advance();
147
148 StringRef bindName = std::get<StringAttr>(*bindNameOpt).getValue();
149 if (insertSymTab.lookup(bindName))
150 return WalkResult::advance();
151
152 OpBuilder builder(mod.getContext());
153 builder.setInsertionPointToEnd(insertBlock);
154 auto funcType = cast<FunctionType>(callee.getFunctionType());
155 func::FuncOp bindFunc =
156 func::FuncOp::create(builder, callee.getLoc(), bindName, funcType);
157 bindFunc.setPrivate();
158 insertSymTab.insert(bindFunc);
159 return WalkResult::advance();
160 });
161 }
162};
163
164class ACCBindRoutine : public acc::impl::ACCBindRoutineBase<ACCBindRoutine> {
165public:
166 using acc::impl::ACCBindRoutineBase<ACCBindRoutine>::ACCBindRoutineBase;
167
168 void getDependentDialects(DialectRegistry &registry) const override {
170 registry);
172 }
173
174 void runOnOperation() override {
175 FunctionOpInterface func = getOperation();
176 ModuleOp module = func->getParentOfType<ModuleOp>();
177 if (!module)
178 return;
179
180 auto cachedAnalysis =
181 getCachedParentAnalysis<OpenACCSupport>(func->getParentOp());
182 OpenACCSupport &accSupport =
183 cachedAnalysis ? cachedAnalysis->get() : getAnalysis<OpenACCSupport>();
184 SymbolTable outerSymTab(module);
185 bool failed = false;
186
187 func.walk([&](CallOpInterface callOp) {
188 if (!callOp.getCallableForCallee())
189 return;
190 if (!callOp->getParentOfType<OffloadRegionOpInterface>() &&
191 !callOp->getParentOfType<gpu::GPUFuncOp>())
192 return;
193 SymbolRefAttr calleeSymbolRef =
194 dyn_cast<SymbolRefAttr>(callOp.getCallableForCallee());
195 if (!calleeSymbolRef)
196 return;
197 FunctionOpInterface callee =
199 callOp, calleeSymbolRef);
200 if (!callee)
201 callee = outerSymTab.lookup<FunctionOpInterface>(
202 calleeSymbolRef.getLeafReference());
203 if (!callee)
204 return;
205
206 if (!(isAccRoutine(callee) || isSpecializedAccRoutine(callee)))
207 return;
208
209 if (auto routineInfo = callee->getDiscardableAttrOfType<RoutineInfoAttr>(
211 if (routineInfo.getAccRoutines().size() > 1) {
212 (void)accSupport.emitNYI(callOp.getLoc(), "multiple `acc routine`s");
213 failed = true;
214 return;
215 }
216 }
217
218 RoutineOp routine = getFirstAccRoutineOp(callee, outerSymTab);
219 if (!routine)
220 return;
221
222 auto bindNameOpt = routine.getEffectiveBindName(this->deviceType);
223 if (!bindNameOpt)
224 return;
225 SymbolRefAttr calleeRef;
226 if (auto *symRef = std::get_if<SymbolRefAttr>(&*bindNameOpt)) {
227 calleeRef = *symRef;
228 } else {
229 StringRef bindName = std::get<StringAttr>(*bindNameOpt).getValue();
230 auto gpuMod = func->getParentOfType<gpu::GPUModuleOp>();
231 Operation *symbolTableOp =
232 gpuMod ? gpuMod.getOperation() : module.getOperation();
233 SymbolTable symbolTable(symbolTableOp);
234 if (!symbolTable.lookup(bindName)) {
235 callOp.emitError(
236 "string-bound ACC routine target was not materialized");
237 failed = true;
238 return;
239 }
240 calleeRef = FlatSymbolRefAttr::get(callOp.getContext(), bindName);
241 }
242 callOp.setCalleeFromCallable(calleeRef);
243 });
244
245 if (failed)
246 signalPassFailure();
247 }
248};
249} // namespace
Block represents an ordered list of Operations.
Definition Block.h:34
static FlatSymbolRefAttr get(StringAttr value)
Construct a symbol reference for the given value name.
mlir::FunctionOpInterface getOperation()
Definition Pass.h:456
This class helps build Operations.
Definition Builders.h:210
A trait used to provide symbol table functionalities to a region operation.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition Operation.h:740
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:803
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
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),...
Definition Operation.h:851
virtual void runOnOperation()=0
The polymorphic API that runs the pass over the currently held operation.
void signalPassFailure()
Signal that some invariant was broken when running.
Definition Pass.h:226
Block & front()
Definition Region.h:65
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition SymbolTable.h:25
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 WalkResult skip()
Definition WalkResult.h:48
static WalkResult advance()
Definition WalkResult.h:47
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
void getDependentDialects(::mlir::DialectRegistry &registry) const override
Return the dialect that must be loaded in the context before this pass.
bool isAccRoutine(mlir::Operation *op)
Used to check whether the current operation is marked with acc routine.
Definition OpenACC.h:211
static constexpr StringLiteral getSpecializedRoutineAttrName()
Definition OpenACC.h:205
bool isSpecializedAccRoutine(mlir::Operation *op)
Used to check whether this is a specialized accelerator version of acc routine function.
Definition OpenACC.h:217
static constexpr StringLiteral getRoutineInfoAttrName()
Definition OpenACC.h:201
void registerAllExtensions(DialectRegistry &registry)
Register the standard OpenACC models for GPU, LLVM and MemRef IR.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:744
Include the generated interface declarations.