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 | |
28 | using namespace mlir; |
29 | |
30 | using 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. |
38 | static constexpr uint64_t kCreateFlag = 0x000; |
39 | static constexpr uint64_t kDeviceCopyinFlag = 0x001; |
40 | static constexpr uint64_t kHostCopyoutFlag = 0x002; |
41 | static constexpr uint64_t kPresentFlag = 0x1000; |
42 | static constexpr uint64_t kDeleteFlag = 0x008; |
43 | // Runtime extension to implement the OpenACC second reference counter. |
44 | static constexpr uint64_t kHoldFlag = 0x2000; |
45 | |
46 | /// Default value for the device id |
47 | static constexpr int64_t kDefaultDevice = -1; |
48 | |
49 | /// Create the location struct from the operation location information. |
50 | static 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. |
62 | static 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. |
82 | static LogicalResult |
83 | processOperands(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 |
142 | static LogicalResult |
143 | processDataOperands(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 |
183 | static LogicalResult |
184 | processDataOperands(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 |
224 | static LogicalResult |
225 | processDataOperands(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. |
263 | static 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. |
423 | template <typename OpTy> |
424 | static LogicalResult |
425 | convertStandaloneDataOp(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 | |
475 | namespace { |
476 | |
477 | /// Implementation of the dialect interface that converts operations belonging |
478 | /// to the OpenACC dialect to LLVM IR. |
479 | class OpenACCDialectLLVMIRTranslationInterface |
480 | : public LLVMTranslationDialectInterface { |
481 | public: |
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). |
495 | LogicalResult 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 | |
533 | void mlir::registerOpenACCDialectTranslation(DialectRegistry ®istry) { |
534 | registry.insert<acc::OpenACCDialect>(); |
535 | registry.addExtension(extensionFn: +[](MLIRContext *ctx, acc::OpenACCDialect *dialect) { |
536 | dialect->addInterfaces<OpenACCDialectLLVMIRTranslationInterface>(); |
537 | }); |
538 | } |
539 | |
540 | void mlir::registerOpenACCDialectTranslation(MLIRContext &context) { |
541 | DialectRegistry registry; |
542 | registerOpenACCDialectTranslation(registry); |
543 | context.appendDialectRegistry(registry); |
544 | } |
545 | |