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