1//===- OpenACCToLLVMIRTranslation.cpp -------------------------------------===//
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// This file implements a translation between the MLIR OpenACC dialect and LLVM
10// IR.
11//
12//===----------------------------------------------------------------------===//
13
14#include "mlir/Target/LLVMIR/Dialect/OpenACC/OpenACCToLLVMIRTranslation.h"
15#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
16#include "mlir/Dialect/OpenACC/OpenACC.h"
17#include "mlir/IR/BuiltinOps.h"
18#include "mlir/IR/Operation.h"
19#include "mlir/Support/LLVM.h"
20#include "mlir/Target/LLVMIR/Dialect/OpenMPCommon.h"
21#include "mlir/Target/LLVMIR/ModuleTranslation.h"
22#include "mlir/Transforms/RegionUtils.h"
23
24#include "llvm/ADT/TypeSwitch.h"
25#include "llvm/Frontend/OpenMP/OMPConstants.h"
26#include "llvm/Support/FormatVariadic.h"
27
28using namespace mlir;
29
30using OpenACCIRBuilder = llvm::OpenMPIRBuilder;
31
32//===----------------------------------------------------------------------===//
33// Utility functions
34//===----------------------------------------------------------------------===//
35
36/// Flag values are extracted from openmp/libomptarget/include/omptarget.h and
37/// mapped to corresponding OpenACC flags.
38static constexpr uint64_t kCreateFlag = 0x000;
39static constexpr uint64_t kDeviceCopyinFlag = 0x001;
40static constexpr uint64_t kHostCopyoutFlag = 0x002;
41static constexpr uint64_t kPresentFlag = 0x1000;
42static constexpr uint64_t kDeleteFlag = 0x008;
43// Runtime extension to implement the OpenACC second reference counter.
44static constexpr uint64_t kHoldFlag = 0x2000;
45
46/// Default value for the device id
47static constexpr int64_t kDefaultDevice = -1;
48
49/// Create the location struct from the operation location information.
50static llvm::Value *createSourceLocationInfo(OpenACCIRBuilder &builder,
51 Operation *op) {
52 auto loc = op->getLoc();
53 auto funcOp = op->getParentOfType<LLVM::LLVMFuncOp>();
54 StringRef funcName = funcOp ? funcOp.getName() : "unknown";
55 uint32_t strLen;
56 llvm::Constant *locStr = mlir::LLVM::createSourceLocStrFromLocation(
57 loc, builder, name: funcName, strLen);
58 return builder.getOrCreateIdent(SrcLocStr: locStr, SrcLocStrSize: strLen);
59}
60
61/// Return the runtime function used to lower the given operation.
62static llvm::Function *getAssociatedFunction(OpenACCIRBuilder &builder,
63 Operation *op) {
64 return llvm::TypeSwitch<Operation *, llvm::Function *>(op)
65 .Case(caseFn: [&](acc::EnterDataOp) {
66 return builder.getOrCreateRuntimeFunctionPtr(
67 FnID: llvm::omp::OMPRTL___tgt_target_data_begin_mapper);
68 })
69 .Case(caseFn: [&](acc::ExitDataOp) {
70 return builder.getOrCreateRuntimeFunctionPtr(
71 FnID: llvm::omp::OMPRTL___tgt_target_data_end_mapper);
72 })
73 .Case(caseFn: [&](acc::UpdateOp) {
74 return builder.getOrCreateRuntimeFunctionPtr(
75 FnID: llvm::omp::OMPRTL___tgt_target_data_update_mapper);
76 });
77 llvm_unreachable("Unknown OpenACC operation");
78}
79
80/// Extract pointer, size and mapping information from operands
81/// to populate the future functions arguments.
82static LogicalResult
83processOperands(llvm::IRBuilderBase &builder,
84 LLVM::ModuleTranslation &moduleTranslation, Operation *op,
85 ValueRange operands, unsigned totalNbOperand,
86 uint64_t operandFlag, SmallVector<uint64_t> &flags,
87 SmallVectorImpl<llvm::Constant *> &names, unsigned &index,
88 struct OpenACCIRBuilder::MapperAllocas &mapperAllocas) {
89 OpenACCIRBuilder *accBuilder = moduleTranslation.getOpenMPBuilder();
90 llvm::LLVMContext &ctx = builder.getContext();
91 auto *i8PtrTy = llvm::PointerType::getUnqual(C&: ctx);
92 auto *arrI8PtrTy = llvm::ArrayType::get(ElementType: i8PtrTy, NumElements: totalNbOperand);
93 auto *i64Ty = llvm::Type::getInt64Ty(C&: ctx);
94 auto *arrI64Ty = llvm::ArrayType::get(ElementType: i64Ty, NumElements: totalNbOperand);
95
96 for (Value data : operands) {
97 llvm::Value *dataValue = moduleTranslation.lookupValue(value: data);
98
99 llvm::Value *dataPtrBase;
100 llvm::Value *dataPtr;
101 llvm::Value *dataSize;
102
103 if (isa<LLVM::LLVMPointerType>(data.getType())) {
104 dataPtrBase = dataValue;
105 dataPtr = dataValue;
106 dataSize = accBuilder->getSizeInBytes(BasePtr: dataValue);
107 } else {
108 return op->emitOpError()
109 << "Data operand must be legalized before translation."
110 << "Unsupported type: " << data.getType();
111 }
112
113 // Store base pointer extracted from operand into the i-th position of
114 // argBase.
115 llvm::Value *ptrBaseGEP = builder.CreateInBoundsGEP(
116 Ty: arrI8PtrTy, Ptr: mapperAllocas.ArgsBase,
117 IdxList: {builder.getInt32(C: 0), builder.getInt32(C: index)});
118 builder.CreateStore(Val: dataPtrBase, Ptr: ptrBaseGEP);
119
120 // Store pointer extracted from operand into the i-th position of args.
121 llvm::Value *ptrGEP = builder.CreateInBoundsGEP(
122 Ty: arrI8PtrTy, Ptr: mapperAllocas.Args,
123 IdxList: {builder.getInt32(C: 0), builder.getInt32(C: index)});
124 builder.CreateStore(Val: dataPtr, Ptr: ptrGEP);
125
126 // Store size extracted from operand into the i-th position of argSizes.
127 llvm::Value *sizeGEP = builder.CreateInBoundsGEP(
128 Ty: arrI64Ty, Ptr: mapperAllocas.ArgSizes,
129 IdxList: {builder.getInt32(C: 0), builder.getInt32(C: index)});
130 builder.CreateStore(Val: dataSize, Ptr: sizeGEP);
131
132 flags.push_back(Elt: operandFlag);
133 llvm::Constant *mapName =
134 mlir::LLVM::createMappingInformation(loc: data.getLoc(), builder&: *accBuilder);
135 names.push_back(Elt: mapName);
136 ++index;
137 }
138 return success();
139}
140
141/// Process data operands from acc::EnterDataOp
142static LogicalResult
143processDataOperands(llvm::IRBuilderBase &builder,
144 LLVM::ModuleTranslation &moduleTranslation,
145 acc::EnterDataOp op, SmallVector<uint64_t> &flags,
146 SmallVectorImpl<llvm::Constant *> &names,
147 struct OpenACCIRBuilder::MapperAllocas &mapperAllocas) {
148 // TODO add `create_zero` and `attach` operands
149
150 unsigned index = 0;
151
152 // Create operands are handled as `alloc` call.
153 // Copyin operands are handled as `to` call.
154 llvm::SmallVector<mlir::Value> create, copyin;
155 for (mlir::Value dataOp : op.getDataClauseOperands()) {
156 if (auto createOp =
157 mlir::dyn_cast_or_null<acc::CreateOp>(dataOp.getDefiningOp())) {
158 create.push_back(createOp.getVarPtr());
159 } else if (auto copyinOp = mlir::dyn_cast_or_null<acc::CopyinOp>(
160 dataOp.getDefiningOp())) {
161 copyin.push_back(copyinOp.getVarPtr());
162 }
163 }
164
165 auto nbTotalOperands = create.size() + copyin.size();
166
167 // Create operands are handled as `alloc` call.
168 if (failed(processOperands(builder, moduleTranslation, op, create,
169 nbTotalOperands, kCreateFlag, flags, names, index,
170 mapperAllocas)))
171 return failure();
172
173 // Copyin operands are handled as `to` call.
174 if (failed(processOperands(builder, moduleTranslation, op, copyin,
175 nbTotalOperands, kDeviceCopyinFlag, flags, names,
176 index, mapperAllocas)))
177 return failure();
178
179 return success();
180}
181
182/// Process data operands from acc::ExitDataOp
183static LogicalResult
184processDataOperands(llvm::IRBuilderBase &builder,
185 LLVM::ModuleTranslation &moduleTranslation,
186 acc::ExitDataOp op, SmallVector<uint64_t> &flags,
187 SmallVectorImpl<llvm::Constant *> &names,
188 struct OpenACCIRBuilder::MapperAllocas &mapperAllocas) {
189 // TODO add `detach` operands
190
191 unsigned index = 0;
192
193 llvm::SmallVector<mlir::Value> deleteOperands, copyoutOperands;
194 for (mlir::Value dataOp : op.getDataClauseOperands()) {
195 if (auto devicePtrOp = mlir::dyn_cast_or_null<acc::GetDevicePtrOp>(
196 dataOp.getDefiningOp())) {
197 for (auto &u : devicePtrOp.getAccPtr().getUses()) {
198 if (mlir::dyn_cast_or_null<acc::DeleteOp>(u.getOwner()))
199 deleteOperands.push_back(devicePtrOp.getVarPtr());
200 else if (mlir::dyn_cast_or_null<acc::CopyoutOp>(u.getOwner()))
201 copyoutOperands.push_back(devicePtrOp.getVarPtr());
202 }
203 }
204 }
205
206 auto nbTotalOperands = deleteOperands.size() + copyoutOperands.size();
207
208 // Delete operands are handled as `delete` call.
209 if (failed(processOperands(builder, moduleTranslation, op, deleteOperands,
210 nbTotalOperands, kDeleteFlag, flags, names, index,
211 mapperAllocas)))
212 return failure();
213
214 // Copyout operands are handled as `from` call.
215 if (failed(processOperands(builder, moduleTranslation, op, copyoutOperands,
216 nbTotalOperands, kHostCopyoutFlag, flags, names,
217 index, mapperAllocas)))
218 return failure();
219
220 return success();
221}
222
223/// Process data operands from acc::UpdateOp
224static LogicalResult
225processDataOperands(llvm::IRBuilderBase &builder,
226 LLVM::ModuleTranslation &moduleTranslation,
227 acc::UpdateOp op, SmallVector<uint64_t> &flags,
228 SmallVectorImpl<llvm::Constant *> &names,
229 struct OpenACCIRBuilder::MapperAllocas &mapperAllocas) {
230 unsigned index = 0;
231
232 // Host operands are handled as `from` call.
233 // Device operands are handled as `to` call.
234 llvm::SmallVector<mlir::Value> from, to;
235 for (mlir::Value dataOp : op.getDataClauseOperands()) {
236 if (auto getDevicePtrOp = mlir::dyn_cast_or_null<acc::GetDevicePtrOp>(
237 dataOp.getDefiningOp())) {
238 from.push_back(getDevicePtrOp.getVarPtr());
239 } else if (auto updateDeviceOp =
240 mlir::dyn_cast_or_null<acc::UpdateDeviceOp>(
241 dataOp.getDefiningOp())) {
242 to.push_back(updateDeviceOp.getVarPtr());
243 }
244 }
245
246 if (failed(processOperands(builder, moduleTranslation, op, from, from.size(),
247 kHostCopyoutFlag, flags, names, index,
248 mapperAllocas)))
249 return failure();
250
251 if (failed(processOperands(builder, moduleTranslation, op, to, to.size(),
252 kDeviceCopyinFlag, flags, names, index,
253 mapperAllocas)))
254 return failure();
255 return success();
256}
257
258//===----------------------------------------------------------------------===//
259// Conversion functions
260//===----------------------------------------------------------------------===//
261
262/// Converts an OpenACC data operation into LLVM IR.
263static LogicalResult convertDataOp(acc::DataOp &op,
264 llvm::IRBuilderBase &builder,
265 LLVM::ModuleTranslation &moduleTranslation) {
266 llvm::LLVMContext &ctx = builder.getContext();
267 auto enclosingFuncOp = op.getOperation()->getParentOfType<LLVM::LLVMFuncOp>();
268 llvm::Function *enclosingFunction =
269 moduleTranslation.lookupFunction(name: enclosingFuncOp.getName());
270
271 OpenACCIRBuilder *accBuilder = moduleTranslation.getOpenMPBuilder();
272
273 llvm::Value *srcLocInfo = createSourceLocationInfo(*accBuilder, op);
274
275 llvm::Function *beginMapperFunc = accBuilder->getOrCreateRuntimeFunctionPtr(
276 FnID: llvm::omp::OMPRTL___tgt_target_data_begin_mapper);
277
278 llvm::Function *endMapperFunc = accBuilder->getOrCreateRuntimeFunctionPtr(
279 FnID: llvm::omp::OMPRTL___tgt_target_data_end_mapper);
280
281 // Number of arguments in the data operation.
282 unsigned totalNbOperand = op.getNumDataOperands();
283
284 struct OpenACCIRBuilder::MapperAllocas mapperAllocas;
285 OpenACCIRBuilder::InsertPointTy allocaIP(
286 &enclosingFunction->getEntryBlock(),
287 enclosingFunction->getEntryBlock().getFirstInsertionPt());
288 accBuilder->createMapperAllocas(Loc: builder.saveIP(), AllocaIP: allocaIP, NumOperands: totalNbOperand,
289 MapperAllocas&: mapperAllocas);
290
291 SmallVector<uint64_t> flags;
292 SmallVector<llvm::Constant *> names;
293 unsigned index = 0;
294
295 // TODO handle no_create, deviceptr and attach operands.
296
297 llvm::SmallVector<mlir::Value> copyin, copyout, create, present,
298 deleteOperands;
299 for (mlir::Value dataOp : op.getDataClauseOperands()) {
300 if (auto devicePtrOp = mlir::dyn_cast_or_null<acc::GetDevicePtrOp>(
301 dataOp.getDefiningOp())) {
302 for (auto &u : devicePtrOp.getAccPtr().getUses()) {
303 if (mlir::dyn_cast_or_null<acc::DeleteOp>(u.getOwner())) {
304 deleteOperands.push_back(devicePtrOp.getVarPtr());
305 } else if (mlir::dyn_cast_or_null<acc::CopyoutOp>(u.getOwner())) {
306 // TODO copyout zero currenlty handled as copyout. Update when
307 // extension available.
308 copyout.push_back(devicePtrOp.getVarPtr());
309 }
310 }
311 } else if (auto copyinOp = mlir::dyn_cast_or_null<acc::CopyinOp>(
312 dataOp.getDefiningOp())) {
313 // TODO copyin readonly currenlty handled as copyin. Update when extension
314 // available.
315 copyin.push_back(copyinOp.getVarPtr());
316 } else if (auto createOp = mlir::dyn_cast_or_null<acc::CreateOp>(
317 dataOp.getDefiningOp())) {
318 // TODO create zero currenlty handled as create. Update when extension
319 // available.
320 create.push_back(createOp.getVarPtr());
321 } else if (auto presentOp = mlir::dyn_cast_or_null<acc::PresentOp>(
322 dataOp.getDefiningOp())) {
323 present.push_back(createOp.getVarPtr());
324 }
325 }
326
327 auto nbTotalOperands = copyin.size() + copyout.size() + create.size() +
328 present.size() + deleteOperands.size();
329
330 // Copyin operands are handled as `to` call.
331 if (failed(processOperands(builder, moduleTranslation, op, copyin,
332 nbTotalOperands, kDeviceCopyinFlag | kHoldFlag,
333 flags, names, index, mapperAllocas)))
334 return failure();
335
336 // Delete operands are handled as `delete` call.
337 if (failed(processOperands(builder, moduleTranslation, op, deleteOperands,
338 nbTotalOperands, kDeleteFlag, flags, names, index,
339 mapperAllocas)))
340 return failure();
341
342 // Copyout operands are handled as `from` call.
343 if (failed(processOperands(builder, moduleTranslation, op, copyout,
344 nbTotalOperands, kHostCopyoutFlag | kHoldFlag,
345 flags, names, index, mapperAllocas)))
346 return failure();
347
348 // Create operands are handled as `alloc` call.
349 if (failed(processOperands(builder, moduleTranslation, op, create,
350 nbTotalOperands, kCreateFlag | kHoldFlag, flags,
351 names, index, mapperAllocas)))
352 return failure();
353
354 if (failed(processOperands(builder, moduleTranslation, op, present,
355 nbTotalOperands, kPresentFlag | kHoldFlag, flags,
356 names, index, mapperAllocas)))
357 return failure();
358
359 llvm::GlobalVariable *maptypes =
360 accBuilder->createOffloadMaptypes(Mappings&: flags, VarName: ".offload_maptypes");
361 llvm::Value *maptypesArg = builder.CreateConstInBoundsGEP2_32(
362 Ty: llvm::ArrayType::get(ElementType: llvm::Type::getInt64Ty(C&: ctx), NumElements: totalNbOperand),
363 Ptr: maptypes, /*Idx0=*/0, /*Idx1=*/0);
364
365 llvm::GlobalVariable *mapnames =
366 accBuilder->createOffloadMapnames(Names&: names, VarName: ".offload_mapnames");
367 llvm::Value *mapnamesArg = builder.CreateConstInBoundsGEP2_32(
368 Ty: llvm::ArrayType::get(ElementType: llvm::PointerType::getUnqual(C&: ctx), NumElements: totalNbOperand),
369 Ptr: mapnames, /*Idx0=*/0, /*Idx1=*/0);
370
371 // Create call to start the data region.
372 accBuilder->emitMapperCall(Loc: builder.saveIP(), MapperFunc: beginMapperFunc, SrcLocInfo: srcLocInfo,
373 MaptypesArg: maptypesArg, MapnamesArg: mapnamesArg, MapperAllocas&: mapperAllocas,
374 DeviceID: kDefaultDevice, NumOperands: totalNbOperand);
375
376 // Convert the region.
377 llvm::BasicBlock *entryBlock = nullptr;
378
379 for (Block &bb : op.getRegion()) {
380 llvm::BasicBlock *llvmBB = llvm::BasicBlock::Create(
381 ctx, "acc.data", builder.GetInsertBlock()->getParent());
382 if (entryBlock == nullptr)
383 entryBlock = llvmBB;
384 moduleTranslation.mapBlock(&bb, llvmBB);
385 }
386
387 auto afterDataRegion = builder.saveIP();
388
389 llvm::BranchInst *sourceTerminator = builder.CreateBr(Dest: entryBlock);
390
391 builder.restoreIP(IP: afterDataRegion);
392 llvm::BasicBlock *endDataBlock = llvm::BasicBlock::Create(
393 Context&: ctx, Name: "acc.end_data", Parent: builder.GetInsertBlock()->getParent());
394
395 SetVector<Block *> blocks = getTopologicallySortedBlocks(op.getRegion());
396 for (Block *bb : blocks) {
397 llvm::BasicBlock *llvmBB = moduleTranslation.lookupBlock(bb);
398 if (bb->isEntryBlock()) {
399 assert(sourceTerminator->getNumSuccessors() == 1 &&
400 "provided entry block has multiple successors");
401 sourceTerminator->setSuccessor(0, llvmBB);
402 }
403
404 if (failed(
405 moduleTranslation.convertBlock(*bb, bb->isEntryBlock(), builder))) {
406 return failure();
407 }
408
409 if (isa<acc::TerminatorOp, acc::YieldOp>(bb->getTerminator()))
410 builder.CreateBr(endDataBlock);
411 }
412
413 // Create call to end the data region.
414 builder.SetInsertPoint(endDataBlock);
415 accBuilder->emitMapperCall(Loc: builder.saveIP(), MapperFunc: endMapperFunc, SrcLocInfo: srcLocInfo,
416 MaptypesArg: maptypesArg, MapnamesArg: mapnamesArg, MapperAllocas&: mapperAllocas,
417 DeviceID: kDefaultDevice, NumOperands: totalNbOperand);
418
419 return success();
420}
421
422/// Converts an OpenACC standalone data operation into LLVM IR.
423template <typename OpTy>
424static LogicalResult
425convertStandaloneDataOp(OpTy &op, llvm::IRBuilderBase &builder,
426 LLVM::ModuleTranslation &moduleTranslation) {
427 auto enclosingFuncOp =
428 op.getOperation()->template getParentOfType<LLVM::LLVMFuncOp>();
429 llvm::Function *enclosingFunction =
430 moduleTranslation.lookupFunction(name: enclosingFuncOp.getName());
431
432 OpenACCIRBuilder *accBuilder = moduleTranslation.getOpenMPBuilder();
433
434 auto *srcLocInfo = createSourceLocationInfo(*accBuilder, op);
435 auto *mapperFunc = getAssociatedFunction(*accBuilder, op);
436
437 // Number of arguments in the enter_data operation.
438 unsigned totalNbOperand = op.getNumDataOperands();
439
440 llvm::LLVMContext &ctx = builder.getContext();
441
442 struct OpenACCIRBuilder::MapperAllocas mapperAllocas;
443 OpenACCIRBuilder::InsertPointTy allocaIP(
444 &enclosingFunction->getEntryBlock(),
445 enclosingFunction->getEntryBlock().getFirstInsertionPt());
446 accBuilder->createMapperAllocas(Loc: builder.saveIP(), AllocaIP: allocaIP, NumOperands: totalNbOperand,
447 MapperAllocas&: mapperAllocas);
448
449 SmallVector<uint64_t> flags;
450 SmallVector<llvm::Constant *> names;
451
452 if (failed(processDataOperands(builder, moduleTranslation, op, flags, names,
453 mapperAllocas)))
454 return failure();
455
456 llvm::GlobalVariable *maptypes =
457 accBuilder->createOffloadMaptypes(Mappings&: flags, VarName: ".offload_maptypes");
458 llvm::Value *maptypesArg = builder.CreateConstInBoundsGEP2_32(
459 Ty: llvm::ArrayType::get(ElementType: llvm::Type::getInt64Ty(C&: ctx), NumElements: totalNbOperand),
460 Ptr: maptypes, /*Idx0=*/0, /*Idx1=*/0);
461
462 llvm::GlobalVariable *mapnames =
463 accBuilder->createOffloadMapnames(Names&: names, VarName: ".offload_mapnames");
464 llvm::Value *mapnamesArg = builder.CreateConstInBoundsGEP2_32(
465 Ty: llvm::ArrayType::get(ElementType: llvm::PointerType::getUnqual(C&: ctx), NumElements: totalNbOperand),
466 Ptr: mapnames, /*Idx0=*/0, /*Idx1=*/0);
467
468 accBuilder->emitMapperCall(Loc: builder.saveIP(), MapperFunc: mapperFunc, SrcLocInfo: srcLocInfo,
469 MaptypesArg: maptypesArg, MapnamesArg: mapnamesArg, MapperAllocas&: mapperAllocas,
470 DeviceID: kDefaultDevice, NumOperands: totalNbOperand);
471
472 return success();
473}
474
475namespace {
476
477/// Implementation of the dialect interface that converts operations belonging
478/// to the OpenACC dialect to LLVM IR.
479class OpenACCDialectLLVMIRTranslationInterface
480 : public LLVMTranslationDialectInterface {
481public:
482 using LLVMTranslationDialectInterface::LLVMTranslationDialectInterface;
483
484 /// Translates the given operation to LLVM IR using the provided IR builder
485 /// and saving the state in `moduleTranslation`.
486 LogicalResult
487 convertOperation(Operation *op, llvm::IRBuilderBase &builder,
488 LLVM::ModuleTranslation &moduleTranslation) const final;
489};
490
491} // namespace
492
493/// Given an OpenACC MLIR operation, create the corresponding LLVM IR
494/// (including OpenACC runtime calls).
495LogicalResult OpenACCDialectLLVMIRTranslationInterface::convertOperation(
496 Operation *op, llvm::IRBuilderBase &builder,
497 LLVM::ModuleTranslation &moduleTranslation) const {
498
499 return llvm::TypeSwitch<Operation *, LogicalResult>(op)
500 .Case([&](acc::DataOp dataOp) {
501 return convertDataOp(dataOp, builder, moduleTranslation);
502 })
503 .Case([&](acc::EnterDataOp enterDataOp) {
504 return convertStandaloneDataOp<acc::EnterDataOp>(enterDataOp, builder,
505 moduleTranslation);
506 })
507 .Case([&](acc::ExitDataOp exitDataOp) {
508 return convertStandaloneDataOp<acc::ExitDataOp>(exitDataOp, builder,
509 moduleTranslation);
510 })
511 .Case([&](acc::UpdateOp updateOp) {
512 return convertStandaloneDataOp<acc::UpdateOp>(updateOp, builder,
513 moduleTranslation);
514 })
515 .Case<acc::TerminatorOp, acc::YieldOp>([](auto op) {
516 // `yield` and `terminator` can be just omitted. The block structure was
517 // created in the function that handles their parent operation.
518 assert(op->getNumOperands() == 0 &&
519 "unexpected OpenACC terminator with operands");
520 return success();
521 })
522 .Case<acc::CreateOp, acc::CopyinOp, acc::CopyoutOp, acc::DeleteOp,
523 acc::UpdateDeviceOp, acc::GetDevicePtrOp>([](auto op) {
524 // NOP
525 return success();
526 })
527 .Default([&](Operation *op) {
528 return op->emitError("unsupported OpenACC operation: ")
529 << op->getName();
530 });
531}
532
533void mlir::registerOpenACCDialectTranslation(DialectRegistry &registry) {
534 registry.insert<acc::OpenACCDialect>();
535 registry.addExtension(extensionFn: +[](MLIRContext *ctx, acc::OpenACCDialect *dialect) {
536 dialect->addInterfaces<OpenACCDialectLLVMIRTranslationInterface>();
537 });
538}
539
540void mlir::registerOpenACCDialectTranslation(MLIRContext &context) {
541 DialectRegistry registry;
542 registerOpenACCDialectTranslation(registry);
543 context.appendDialectRegistry(registry);
544}
545

source code of mlir/lib/Target/LLVMIR/Dialect/OpenACC/OpenACCToLLVMIRTranslation.cpp