75#include "llvm/ADT/DenseMap.h"
76#include "llvm/ADT/SetVector.h"
81#define GEN_PASS_DEF_ACCROUTINETOGPUFUNC
82#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
86#define DEBUG_TYPE "acc-routine-to-gpu-func"
94static gpu::GPUFuncOp createGPUFuncFromFunc(
OpBuilder &builder,
95 func::FuncOp sourceFunc) {
97 StringRef name = sourceFunc.getName();
98 FunctionType type = sourceFunc.getFunctionType();
101 gpu::GPUFuncOp gpuFunc =
102 gpu::GPUFuncOp::create(builder, loc, name, type,
106 Region &sourceBody = sourceFunc.getBody();
107 Region &deviceBody = gpuFunc.getBody();
114 for (
auto [srcArg, destArg] : llvm::zip(sourceEntryBlock.
getArguments(),
116 mapping.
map(srcArg, destArg);
118 sourceBody.cloneInto(&deviceBody, mapping);
121 gpuFunc.walk([](func::ReturnOp op) {
123 gpu::ReturnOp gpuReturn = gpu::ReturnOp::create(replacer, op.getLoc());
124 gpuReturn->setOperands(op.getOperands());
131 Block *clonedSourceEntry = mapping.
lookup(&sourceEntryBlock);
135 clonedSourceEntry->
erase();
140using CloneCandidate = std::pair<func::FuncOp, RoutineOp>;
144static void collectRoutineCandidates(
145 ModuleOp mod,
SymbolTable &symTab, acc::DeviceType deviceType,
147 llvm::SmallSetVector<llvm::StringRef, 4> &funcsToCloneCandidates,
148 llvm::SmallSetVector<RoutineOp, 4> &materializedAccRoutines,
149 llvm::SmallSetVector<RoutineOp, 4> &bindAccRoutines) {
150 auto isParallelRoutine = [deviceType](RoutineOp routineOp) {
151 return routineOp.hasGang(deviceType) || routineOp.hasGang() ||
152 routineOp.hasWorker(deviceType) || routineOp.hasWorker() ||
153 routineOp.hasVector(deviceType) || routineOp.hasVector() ||
154 routineOp.getGangDimValue(deviceType) || routineOp.getGangDimValue();
157 mod.walk([&](RoutineOp op) {
158 if (op.getBindNameValue() || op.getBindNameValue(deviceType)) {
159 bindAccRoutines.insert(op);
162 func::FuncOp callee =
163 symTab.
lookup<func::FuncOp>(op.getFuncName().getLeafReference());
165 callee ? callee.getOperation() : op.getOperation(),
166 [&op, &isParallelRoutine]() {
167 std::string msg =
"Generating";
168 if (op.getImplicitAttr())
170 msg +=
" acc routine";
171 if (!isParallelRoutine(op))
176 funcsToCloneCandidates.insert(op.getFuncName().getLeafReference());
177 materializedAccRoutines.insert(op);
183static LogicalResult processCallsInRoutines(
185 const llvm::SmallSetVector<llvm::StringRef, 4> &funcsToCloneCandidates,
186 const llvm::SmallSetVector<RoutineOp, 4> &materializedAccRoutines,
187 llvm::SmallSetVector<CloneCandidate, 4> &funcsToClone) {
188 LogicalResult callCheckResult =
success();
189 auto processCalls = [&](CallOpInterface callOp) {
190 if (!callOp.getCallableForCallee())
192 auto calleeSymbolRef =
193 dyn_cast<SymbolRefAttr>(callOp.getCallableForCallee());
194 if (!calleeSymbolRef)
198 symTab.
lookup<func::FuncOp>(calleeSymbolRef.getLeafReference());
202 if (gpuSymTab.
lookup(callee.getName()))
208 accSupport.
emitNYI(callOp->getLoc(),
"Unsupported call in acc routine");
209 callCheckResult = failure();
212 funcsToClone.insert({callee, RoutineOp{}});
215 for (
auto [funcName, accRoutine] :
216 llvm::zip(funcsToCloneCandidates, materializedAccRoutines)) {
217 func::FuncOp
func = symTab.
lookup<func::FuncOp>(funcName);
220 if (!gpuSymTab.
lookup(funcName))
221 funcsToClone.insert({
func, accRoutine});
222 func.walk([&](CallOpInterface callOp) { processCalls(callOp); });
223 if (failed(callCheckResult))
233rewriteSpecializedSymbolUses(ModuleOp mod,
240 [&](SymbolRefAttr attr) -> std::pair<Attribute, WalkResult> {
241 auto it = renames.find(attr.getRootReference());
242 if (it == renames.end())
244 if (isa<FlatSymbolRefAttr>(attr))
246 return {SymbolRefAttr::get(it->second, attr.getNestedReferences()),
252 for (
Region ®ion : mod->getRegions()) {
265static LogicalResult cloneFuncsToGPUModule(
267 const llvm::SmallSetVector<CloneCandidate, 4> &funcsToClone) {
275 for (CloneCandidate candidate : funcsToClone) {
276 func::FuncOp srcFunc = candidate.first;
277 if (srcFunc.isDeclaration())
280 srcFunc->getDiscardableAttrOfType<SpecializedRoutineAttr>(
282 StringAttr destName = specAttr.getFuncName();
283 if (srcFunc.getNameAttr() != destName)
284 specializedRenames.try_emplace(srcFunc.getNameAttr(), destName);
287 rewriteSpecializedSymbolUses(mod, specializedRenames);
289 for (CloneCandidate candidate : funcsToClone) {
290 func::FuncOp srcFunc = candidate.first;
292 if (srcFunc.isDeclaration()) {
298 gpu::GPUFuncOp deviceFuncOp = createGPUFuncFromFunc(builder, srcFunc);
301 srcFunc->getDiscardableAttrOfType<SpecializedRoutineAttr>(
303 deviceFuncOp.setName(specAttr.getFuncName());
308 gpuSymTab.
insert(deviceFuncOp);
315cleanupHostModule(
const llvm::SmallSetVector<CloneCandidate, 4> &funcsToClone) {
316 for (CloneCandidate candidate : funcsToClone) {
317 func::FuncOp funcCandidate = candidate.first;
318 RoutineOp routineCandidate = candidate.second;
319 if ((routineCandidate && routineCandidate.getNohost()) ||
321 funcCandidate.erase();
325class ACCRoutineToGPUFunc
329 ACCRoutineToGPUFunc>::ACCRoutineToGPUFuncBase;
331 void runOnOperation()
override {
332 ModuleOp mod = getOperation();
333 if (mod.getOps<RoutineOp>().empty()) {
334 LLVM_DEBUG(llvm::dbgs()
335 <<
"Skipping ACCRoutineToGPUFunc - no acc.routine ops\n");
340 std::optional<gpu::GPUModuleOp> gpuModOpt =
343 accSupport.
emitNYI(mod.getLoc(),
"Failed to create GPU module");
344 return signalPassFailure();
346 gpu::GPUModuleOp gpuMod = *gpuModOpt;
351 llvm::SmallSetVector<llvm::StringRef, 4> funcsToCloneCandidates;
352 llvm::SmallSetVector<RoutineOp, 4> materializedAccRoutines;
353 llvm::SmallSetVector<RoutineOp, 4> bindAccRoutines;
355 collectRoutineCandidates(mod, symTab, this->deviceType, accSupport,
356 funcsToCloneCandidates, materializedAccRoutines,
359 llvm::SmallSetVector<CloneCandidate, 4> funcsToClone;
360 if (failed(processCallsInRoutines(symTab, gpuSymTab, accSupport,
361 funcsToCloneCandidates,
362 materializedAccRoutines, funcsToClone)))
363 return signalPassFailure();
365 if (failed(cloneFuncsToGPUModule(mod, gpuSymTab, funcsToClone)))
366 return signalPassFailure();
368 cleanupHostModule(funcsToClone);
This is an attribute/type replacer that is naively cached.
Block represents an ordered list of Operations.
void erase()
Unlink this Block from its parent region and delete it.
OpListType & getOperations()
BlockArgListType getArguments()
static FlatSymbolRefAttr get(StringAttr value)
Construct a symbol reference for the given value name.
This is a utility class for mapping one set of IR entities to another.
auto lookup(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
A trait used to provide symbol table functionalities to a region operation.
Operation is the basic unit of execution within MLIR.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Operation * clone(IRMapping &mapper, const CloneOptions &options=CloneOptions::all())
Create a deep copy of this operation, remapping any operands that use values outside of the operation...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
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.
StringAttr insert(Operation *symbol, Block::iterator insertPt={})
Insert a new symbol into the table, and rename it as necessary to avoid collisions.
static WalkResult advance()
remark::detail::InFlightRemark emitRemark(Operation *op, std::function< std::string()> messageFn, llvm::StringRef category="openacc")
Emit an OpenACC remark with lazy message generation.
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
bool isValidSymbolUse(Operation *user, SymbolRefAttr symbol, Operation **definingOpPtr=nullptr)
Check if a symbol use is valid for use in an OpenACC region.
std::optional< gpu::GPUModuleOp > getOrCreateGPUModule(ModuleOp mod, bool create=true, llvm::StringRef name="")
Get or optionally create a GPU module in the given module.
void addReplacement(ReplaceFn< Attribute > fn)
AttrTypeReplacerBase.
void replaceElementsIn(Operation *op, bool replaceAttrs=true, bool replaceLocs=false, bool replaceTypes=false)
Replace the elements within the given operation.
bool isAccRoutine(mlir::Operation *op)
Used to check whether the current operation is marked with acc routine.
static constexpr StringLiteral getSpecializedRoutineAttrName()
bool isSpecializedAccRoutine(mlir::Operation *op)
Used to check whether this is a specialized accelerator version of acc routine function.
Include the generated interface declarations.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap