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
32
37#include "mlir/IR/SymbolTable.h"
39#include "llvm/Support/Debug.h"
40
41namespace mlir {
42namespace acc {
43#define GEN_PASS_DEF_ACCBINDROUTINE
44#define GEN_PASS_DEF_ACCMATERIALIZEROUTINEBINDTARGETS
45#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
46} // namespace acc
47} // namespace mlir
48
49#define DEBUG_TYPE "acc-bind-routine"
50
51using namespace mlir;
52using namespace mlir::acc;
53
54namespace {
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}
79static bool isACCRoutineBindDefaultOrDeviceType(RoutineOp op,
80 DeviceType deviceType) {
81 if (!op.getBindIdName() && !op.getBindStrName())
82 return false;
83 return op.getBindNameValue().has_value() ||
84 op.getBindNameValue(deviceType).has_value();
85}
87class ACCMaterializeRoutineBindTargets
89 ACCMaterializeRoutineBindTargets> {
91 ACCMaterializeRoutineBindTargets>;
92
93public:
94 using Base::Base;
95
96 void runOnOperation() override {
97 Operation *symbolTableOp = getOperation();
98 if (!symbolTableOp->hasTrait<OpTrait::SymbolTable>()) {
99 symbolTableOp->emitError(
100 "root operation must have the SymbolTable trait");
102 return;
103 }
105 ModuleOp mod = dyn_cast<ModuleOp>(symbolTableOp);
106 if (!mod)
107 mod = symbolTableOp->getParentOfType<ModuleOp>();
108 if (!mod) {
109 symbolTableOp->emitError(
110 "root operation must be a builtin.module or nested in one");
112 return;
114
115 SymbolTable outerSymTab(mod);
116 SymbolTable insertSymTab(symbolTableOp);
117 Block *insertBlock = &symbolTableOp->getRegion(0).front();
118
119 symbolTableOp->walk<WalkOrder::PreOrder>([&](Operation *op) {
120 if (op != symbolTableOp && op->hasTrait<OpTrait::SymbolTable>())
121 return WalkResult::skip();
122
123 auto callOp = dyn_cast<CallOpInterface>(op);
124 if (!callOp || !callOp.getCallableForCallee())
125 return WalkResult::advance();
126 if (!callOp->getParentOfType<OffloadRegionOpInterface>() &&
127 !callOp->getParentOfType<gpu::GPUFuncOp>())
128 return WalkResult::advance();
129
130 auto calleeSymbolRef =
131 dyn_cast<SymbolRefAttr>(callOp.getCallableForCallee());
132 if (!calleeSymbolRef)
133 return WalkResult::advance();
134 FunctionOpInterface callee =
136 callOp, calleeSymbolRef);
137 if (!callee)
138 callee = outerSymTab.lookup<FunctionOpInterface>(
139 calleeSymbolRef.getLeafReference());
140 if (!callee || !(isAccRoutine(callee) || isSpecializedAccRoutine(callee)))
141 return WalkResult::advance();
142
143 if (auto routineInfo = callee->getDiscardableAttrOfType<RoutineInfoAttr>(
145 if (routineInfo.getAccRoutines().size() > 1)
146 return WalkResult::advance();
147
148 RoutineOp routine = getFirstAccRoutineOp(callee, outerSymTab);
149 if (!routine ||
150 !isACCRoutineBindDefaultOrDeviceType(routine, this->deviceType))
151 return WalkResult::advance();
152
153 auto bindNameOpt = routine.getBindNameValue(this->deviceType);
154 if (!bindNameOpt)
155 bindNameOpt = routine.getBindNameValue();
156 if (!bindNameOpt || !std::holds_alternative<StringAttr>(*bindNameOpt))
157 return WalkResult::advance();
158
159 StringRef bindName = std::get<StringAttr>(*bindNameOpt).getValue();
160 if (insertSymTab.lookup(bindName))
161 return WalkResult::advance();
162
163 OpBuilder builder(mod.getContext());
164 builder.setInsertionPointToEnd(insertBlock);
165 auto funcType = cast<FunctionType>(callee.getFunctionType());
166 func::FuncOp bindFunc =
167 func::FuncOp::create(builder, callee.getLoc(), bindName, funcType);
168 bindFunc.setPrivate();
169 insertSymTab.insert(bindFunc);
170 return WalkResult::advance();
171 });
172 }
173};
174
175class ACCBindRoutine : public acc::impl::ACCBindRoutineBase<ACCBindRoutine> {
176public:
177 using acc::impl::ACCBindRoutineBase<ACCBindRoutine>::ACCBindRoutineBase;
178
179 void runOnOperation() override {
180 FunctionOpInterface func = getOperation();
181 ModuleOp module = func->getParentOfType<ModuleOp>();
182 if (!module)
183 return;
184
185 auto cachedAnalysis =
186 getCachedParentAnalysis<OpenACCSupport>(func->getParentOp());
187 OpenACCSupport &accSupport =
188 cachedAnalysis ? cachedAnalysis->get() : getAnalysis<OpenACCSupport>();
189 SymbolTable outerSymTab(module);
190 bool failed = false;
191
192 func.walk([&](CallOpInterface callOp) {
193 if (!callOp.getCallableForCallee())
194 return;
195 if (!callOp->getParentOfType<OffloadRegionOpInterface>() &&
196 !callOp->getParentOfType<gpu::GPUFuncOp>())
197 return;
198 SymbolRefAttr calleeSymbolRef =
199 dyn_cast<SymbolRefAttr>(callOp.getCallableForCallee());
200 if (!calleeSymbolRef)
201 return;
202 FunctionOpInterface callee =
204 callOp, calleeSymbolRef);
205 if (!callee)
206 callee = outerSymTab.lookup<FunctionOpInterface>(
207 calleeSymbolRef.getLeafReference());
208 if (!callee)
209 return;
210
211 if (!(isAccRoutine(callee) || isSpecializedAccRoutine(callee)))
212 return;
213
214 if (auto routineInfo = callee->getDiscardableAttrOfType<RoutineInfoAttr>(
216 if (routineInfo.getAccRoutines().size() > 1) {
217 (void)accSupport.emitNYI(callOp.getLoc(), "multiple `acc routine`s");
218 failed = true;
219 return;
220 }
221 }
222
223 RoutineOp routine = getFirstAccRoutineOp(callee, outerSymTab);
224 if (!routine ||
225 !isACCRoutineBindDefaultOrDeviceType(routine, this->deviceType))
226 return;
227
228 auto bindNameOpt = routine.getBindNameValue(this->deviceType);
229 if (!bindNameOpt)
230 bindNameOpt = routine.getBindNameValue();
231 if (!bindNameOpt)
232 return;
233 SymbolRefAttr calleeRef;
234 if (auto *symRef = std::get_if<SymbolRefAttr>(&*bindNameOpt)) {
235 calleeRef = *symRef;
236 } else {
237 StringRef bindName = std::get<StringAttr>(*bindNameOpt).getValue();
238 auto gpuMod = func->getParentOfType<gpu::GPUModuleOp>();
239 Operation *symbolTableOp =
240 gpuMod ? gpuMod.getOperation() : module.getOperation();
241 SymbolTable symbolTable(symbolTableOp);
242 if (!symbolTable.lookup(bindName)) {
243 callOp.emitError(
244 "string-bound ACC routine target was not materialized");
245 failed = true;
246 return;
247 }
248 calleeRef = FlatSymbolRefAttr::get(callOp.getContext(), bindName);
249 }
250 callOp.setCalleeFromCallable(calleeRef);
251 });
252
253 if (failed)
254 signalPassFailure();
255 }
256};
257
258} // 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:738
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:801
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:849
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.
::mlir::Pass::Option< mlir::acc::DeviceType > deviceType
bool isAccRoutine(mlir::Operation *op)
Used to check whether the current operation is marked with acc routine.
Definition OpenACC.h:214
static constexpr StringLiteral getSpecializedRoutineAttrName()
Definition OpenACC.h:208
bool isSpecializedAccRoutine(mlir::Operation *op)
Used to check whether this is a specialized accelerator version of acc routine function.
Definition OpenACC.h:220
static constexpr StringLiteral getRoutineInfoAttrName()
Definition OpenACC.h:204
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
Include the generated interface declarations.