1 //===- OpenMPToLLVMIRTranslation.cpp - Translate OpenMP dialect to LLVM IR-===// 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 OpenMP dialect and LLVM 10 // IR. 11 // 12 //===----------------------------------------------------------------------===// 13 #include "mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h" 14 #include "mlir/Dialect/OpenMP/OpenMPDialect.h" 15 #include "mlir/IR/BlockAndValueMapping.h" 16 #include "mlir/IR/Operation.h" 17 #include "mlir/Support/LLVM.h" 18 #include "mlir/Target/LLVMIR/ModuleTranslation.h" 19 20 #include "llvm/ADT/SetVector.h" 21 #include "llvm/ADT/TypeSwitch.h" 22 #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" 23 #include "llvm/IR/IRBuilder.h" 24 25 using namespace mlir; 26 27 namespace { 28 /// ModuleTranslation stack frame for OpenMP operations. This keeps track of the 29 /// insertion points for allocas. 30 class OpenMPAllocaStackFrame 31 : public LLVM::ModuleTranslation::StackFrameBase<OpenMPAllocaStackFrame> { 32 public: 33 explicit OpenMPAllocaStackFrame(llvm::OpenMPIRBuilder::InsertPointTy allocaIP) 34 : allocaInsertPoint(allocaIP) {} 35 llvm::OpenMPIRBuilder::InsertPointTy allocaInsertPoint; 36 }; 37 38 /// ModuleTranslation stack frame containing the partial mapping between MLIR 39 /// values and their LLVM IR equivalents. 40 class OpenMPVarMappingStackFrame 41 : public LLVM::ModuleTranslation::StackFrameBase< 42 OpenMPVarMappingStackFrame> { 43 public: 44 explicit OpenMPVarMappingStackFrame( 45 const DenseMap<Value, llvm::Value *> &mapping) 46 : mapping(mapping) {} 47 48 DenseMap<Value, llvm::Value *> mapping; 49 }; 50 } // namespace 51 52 /// Find the insertion point for allocas given the current insertion point for 53 /// normal operations in the builder. 54 static llvm::OpenMPIRBuilder::InsertPointTy 55 findAllocaInsertPoint(llvm::IRBuilderBase &builder, 56 const LLVM::ModuleTranslation &moduleTranslation) { 57 // If there is an alloca insertion point on stack, i.e. we are in a nested 58 // operation and a specific point was provided by some surrounding operation, 59 // use it. 60 llvm::OpenMPIRBuilder::InsertPointTy allocaInsertPoint; 61 WalkResult walkResult = moduleTranslation.stackWalk<OpenMPAllocaStackFrame>( 62 [&](const OpenMPAllocaStackFrame &frame) { 63 allocaInsertPoint = frame.allocaInsertPoint; 64 return WalkResult::interrupt(); 65 }); 66 if (walkResult.wasInterrupted()) 67 return allocaInsertPoint; 68 69 // Otherwise, insert to the entry block of the surrounding function. 70 llvm::BasicBlock &funcEntryBlock = 71 builder.GetInsertBlock()->getParent()->getEntryBlock(); 72 return llvm::OpenMPIRBuilder::InsertPointTy( 73 &funcEntryBlock, funcEntryBlock.getFirstInsertionPt()); 74 } 75 76 /// Converts the given region that appears within an OpenMP dialect operation to 77 /// LLVM IR, creating a branch from the `sourceBlock` to the entry block of the 78 /// region, and a branch from any block with an successor-less OpenMP terminator 79 /// to `continuationBlock`. Populates `continuationBlockPHIs` with the PHI nodes 80 /// of the continuation block if provided. 81 static void convertOmpOpRegions( 82 Region ®ion, StringRef blockName, llvm::BasicBlock &sourceBlock, 83 llvm::BasicBlock &continuationBlock, llvm::IRBuilderBase &builder, 84 LLVM::ModuleTranslation &moduleTranslation, LogicalResult &bodyGenStatus, 85 SmallVectorImpl<llvm::PHINode *> *continuationBlockPHIs = nullptr) { 86 llvm::LLVMContext &llvmContext = builder.getContext(); 87 for (Block &bb : region) { 88 llvm::BasicBlock *llvmBB = llvm::BasicBlock::Create( 89 llvmContext, blockName, builder.GetInsertBlock()->getParent(), 90 builder.GetInsertBlock()->getNextNode()); 91 moduleTranslation.mapBlock(&bb, llvmBB); 92 } 93 94 llvm::Instruction *sourceTerminator = sourceBlock.getTerminator(); 95 96 // Terminators (namely YieldOp) may be forwarding values to the region that 97 // need to be available in the continuation block. Collect the types of these 98 // operands in preparation of creating PHI nodes. 99 SmallVector<llvm::Type *> continuationBlockPHITypes; 100 bool operandsProcessed = false; 101 unsigned numYields = 0; 102 for (Block &bb : region.getBlocks()) { 103 if (omp::YieldOp yield = dyn_cast<omp::YieldOp>(bb.getTerminator())) { 104 if (!operandsProcessed) { 105 for (unsigned i = 0, e = yield->getNumOperands(); i < e; ++i) { 106 continuationBlockPHITypes.push_back( 107 moduleTranslation.convertType(yield->getOperand(i).getType())); 108 } 109 operandsProcessed = true; 110 } else { 111 assert(continuationBlockPHITypes.size() == yield->getNumOperands() && 112 "mismatching number of values yielded from the region"); 113 for (unsigned i = 0, e = yield->getNumOperands(); i < e; ++i) { 114 llvm::Type *operandType = 115 moduleTranslation.convertType(yield->getOperand(i).getType()); 116 (void)operandType; 117 assert(continuationBlockPHITypes[i] == operandType && 118 "values of mismatching types yielded from the region"); 119 } 120 } 121 numYields++; 122 } 123 } 124 125 // Insert PHI nodes in the continuation block for any values forwarded by the 126 // terminators in this region. 127 if (!continuationBlockPHITypes.empty()) 128 assert( 129 continuationBlockPHIs && 130 "expected continuation block PHIs if converted regions yield values"); 131 if (continuationBlockPHIs) { 132 llvm::IRBuilderBase::InsertPointGuard guard(builder); 133 continuationBlockPHIs->reserve(continuationBlockPHITypes.size()); 134 builder.SetInsertPoint(&continuationBlock, continuationBlock.begin()); 135 for (llvm::Type *ty : continuationBlockPHITypes) 136 continuationBlockPHIs->push_back(builder.CreatePHI(ty, numYields)); 137 } 138 139 // Convert blocks one by one in topological order to ensure 140 // defs are converted before uses. 141 SetVector<Block *> blocks = 142 LLVM::detail::getTopologicallySortedBlocks(region); 143 for (Block *bb : blocks) { 144 llvm::BasicBlock *llvmBB = moduleTranslation.lookupBlock(bb); 145 // Retarget the branch of the entry block to the entry block of the 146 // converted region (regions are single-entry). 147 if (bb->isEntryBlock()) { 148 assert(sourceTerminator->getNumSuccessors() == 1 && 149 "provided entry block has multiple successors"); 150 assert(sourceTerminator->getSuccessor(0) == &continuationBlock && 151 "ContinuationBlock is not the successor of the entry block"); 152 sourceTerminator->setSuccessor(0, llvmBB); 153 } 154 155 llvm::IRBuilderBase::InsertPointGuard guard(builder); 156 if (failed( 157 moduleTranslation.convertBlock(*bb, bb->isEntryBlock(), builder))) { 158 bodyGenStatus = failure(); 159 return; 160 } 161 162 // Special handling for `omp.yield` and `omp.terminator` (we may have more 163 // than one): they return the control to the parent OpenMP dialect operation 164 // so replace them with the branch to the continuation block. We handle this 165 // here to avoid relying inter-function communication through the 166 // ModuleTranslation class to set up the correct insertion point. This is 167 // also consistent with MLIR's idiom of handling special region terminators 168 // in the same code that handles the region-owning operation. 169 Operation *terminator = bb->getTerminator(); 170 if (isa<omp::TerminatorOp, omp::YieldOp>(terminator)) { 171 builder.CreateBr(&continuationBlock); 172 173 for (unsigned i = 0, e = terminator->getNumOperands(); i < e; ++i) 174 (*continuationBlockPHIs)[i]->addIncoming( 175 moduleTranslation.lookupValue(terminator->getOperand(i)), llvmBB); 176 } 177 } 178 // After all blocks have been traversed and values mapped, connect the PHI 179 // nodes to the results of preceding blocks. 180 LLVM::detail::connectPHINodes(region, moduleTranslation); 181 182 // Remove the blocks and values defined in this region from the mapping since 183 // they are not visible outside of this region. This allows the same region to 184 // be converted several times, that is cloned, without clashes, and slightly 185 // speeds up the lookups. 186 moduleTranslation.forgetMapping(region); 187 } 188 189 /// Converts the OpenMP parallel operation to LLVM IR. 190 static LogicalResult 191 convertOmpParallel(Operation &opInst, llvm::IRBuilderBase &builder, 192 LLVM::ModuleTranslation &moduleTranslation) { 193 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy; 194 // TODO: support error propagation in OpenMPIRBuilder and use it instead of 195 // relying on captured variables. 196 LogicalResult bodyGenStatus = success(); 197 198 auto bodyGenCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP, 199 llvm::BasicBlock &continuationBlock) { 200 // Save the alloca insertion point on ModuleTranslation stack for use in 201 // nested regions. 202 LLVM::ModuleTranslation::SaveStack<OpenMPAllocaStackFrame> frame( 203 moduleTranslation, allocaIP); 204 205 // ParallelOp has only one region associated with it. 206 auto ®ion = cast<omp::ParallelOp>(opInst).getRegion(); 207 convertOmpOpRegions(region, "omp.par.region", *codeGenIP.getBlock(), 208 continuationBlock, builder, moduleTranslation, 209 bodyGenStatus); 210 }; 211 212 // TODO: Perform appropriate actions according to the data-sharing 213 // attribute (shared, private, firstprivate, ...) of variables. 214 // Currently defaults to shared. 215 auto privCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP, 216 llvm::Value &, llvm::Value &vPtr, 217 llvm::Value *&replacementValue) -> InsertPointTy { 218 replacementValue = &vPtr; 219 220 return codeGenIP; 221 }; 222 223 // TODO: Perform finalization actions for variables. This has to be 224 // called for variables which have destructors/finalizers. 225 auto finiCB = [&](InsertPointTy codeGenIP) {}; 226 227 llvm::Value *ifCond = nullptr; 228 if (auto ifExprVar = cast<omp::ParallelOp>(opInst).if_expr_var()) 229 ifCond = moduleTranslation.lookupValue(ifExprVar); 230 llvm::Value *numThreads = nullptr; 231 if (auto numThreadsVar = cast<omp::ParallelOp>(opInst).num_threads_var()) 232 numThreads = moduleTranslation.lookupValue(numThreadsVar); 233 llvm::omp::ProcBindKind pbKind = llvm::omp::OMP_PROC_BIND_default; 234 if (auto bind = cast<omp::ParallelOp>(opInst).proc_bind_val()) 235 pbKind = llvm::omp::getProcBindKind(bind.getValue()); 236 // TODO: Is the Parallel construct cancellable? 237 bool isCancellable = false; 238 239 llvm::OpenMPIRBuilder::LocationDescription ompLoc( 240 builder.saveIP(), builder.getCurrentDebugLocation()); 241 builder.restoreIP(moduleTranslation.getOpenMPBuilder()->createParallel( 242 ompLoc, findAllocaInsertPoint(builder, moduleTranslation), bodyGenCB, 243 privCB, finiCB, ifCond, numThreads, pbKind, isCancellable)); 244 245 return bodyGenStatus; 246 } 247 248 /// Converts an OpenMP 'master' operation into LLVM IR using OpenMPIRBuilder. 249 static LogicalResult 250 convertOmpMaster(Operation &opInst, llvm::IRBuilderBase &builder, 251 LLVM::ModuleTranslation &moduleTranslation) { 252 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy; 253 // TODO: support error propagation in OpenMPIRBuilder and use it instead of 254 // relying on captured variables. 255 LogicalResult bodyGenStatus = success(); 256 257 auto bodyGenCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP, 258 llvm::BasicBlock &continuationBlock) { 259 // MasterOp has only one region associated with it. 260 auto ®ion = cast<omp::MasterOp>(opInst).getRegion(); 261 convertOmpOpRegions(region, "omp.master.region", *codeGenIP.getBlock(), 262 continuationBlock, builder, moduleTranslation, 263 bodyGenStatus); 264 }; 265 266 // TODO: Perform finalization actions for variables. This has to be 267 // called for variables which have destructors/finalizers. 268 auto finiCB = [&](InsertPointTy codeGenIP) {}; 269 270 llvm::OpenMPIRBuilder::LocationDescription ompLoc( 271 builder.saveIP(), builder.getCurrentDebugLocation()); 272 builder.restoreIP(moduleTranslation.getOpenMPBuilder()->createMaster( 273 ompLoc, bodyGenCB, finiCB)); 274 return success(); 275 } 276 277 /// Converts an OpenMP 'critical' operation into LLVM IR using OpenMPIRBuilder. 278 static LogicalResult 279 convertOmpCritical(Operation &opInst, llvm::IRBuilderBase &builder, 280 LLVM::ModuleTranslation &moduleTranslation) { 281 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy; 282 auto criticalOp = cast<omp::CriticalOp>(opInst); 283 // TODO: support error propagation in OpenMPIRBuilder and use it instead of 284 // relying on captured variables. 285 LogicalResult bodyGenStatus = success(); 286 287 auto bodyGenCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP, 288 llvm::BasicBlock &continuationBlock) { 289 // CriticalOp has only one region associated with it. 290 auto ®ion = cast<omp::CriticalOp>(opInst).getRegion(); 291 convertOmpOpRegions(region, "omp.critical.region", *codeGenIP.getBlock(), 292 continuationBlock, builder, moduleTranslation, 293 bodyGenStatus); 294 }; 295 296 // TODO: Perform finalization actions for variables. This has to be 297 // called for variables which have destructors/finalizers. 298 auto finiCB = [&](InsertPointTy codeGenIP) {}; 299 300 llvm::OpenMPIRBuilder::LocationDescription ompLoc( 301 builder.saveIP(), builder.getCurrentDebugLocation()); 302 llvm::LLVMContext &llvmContext = moduleTranslation.getLLVMContext(); 303 llvm::Constant *hint = nullptr; 304 305 // If it has a name, it probably has a hint too. 306 if (criticalOp.nameAttr()) { 307 // The verifiers in OpenMP Dialect guarentee that all the pointers are 308 // non-null 309 auto symbolRef = criticalOp.nameAttr().cast<SymbolRefAttr>(); 310 auto criticalDeclareOp = 311 SymbolTable::lookupNearestSymbolFrom<omp::CriticalDeclareOp>(criticalOp, 312 symbolRef); 313 hint = llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvmContext), 314 static_cast<int>(criticalDeclareOp.hint())); 315 } 316 builder.restoreIP(moduleTranslation.getOpenMPBuilder()->createCritical( 317 ompLoc, bodyGenCB, finiCB, criticalOp.name().getValueOr(""), hint)); 318 return success(); 319 } 320 321 /// Returns a reduction declaration that corresponds to the given reduction 322 /// operation in the given container. Currently only supports reductions inside 323 /// WsLoopOp but can be easily extended. 324 static omp::ReductionDeclareOp findReductionDecl(omp::WsLoopOp container, 325 omp::ReductionOp reduction) { 326 SymbolRefAttr reductionSymbol; 327 for (unsigned i = 0, e = container.getNumReductionVars(); i < e; ++i) { 328 if (container.reduction_vars()[i] != reduction.accumulator()) 329 continue; 330 reductionSymbol = (*container.reductions())[i].cast<SymbolRefAttr>(); 331 break; 332 } 333 assert(reductionSymbol && 334 "reduction operation must be associated with a declaration"); 335 336 return SymbolTable::lookupNearestSymbolFrom<omp::ReductionDeclareOp>( 337 container, reductionSymbol); 338 } 339 340 /// Populates `reductions` with reduction declarations used in the given loop. 341 static void 342 collectReductionDecls(omp::WsLoopOp loop, 343 SmallVectorImpl<omp::ReductionDeclareOp> &reductions) { 344 Optional<ArrayAttr> attr = loop.reductions(); 345 if (!attr) 346 return; 347 348 reductions.reserve(reductions.size() + loop.getNumReductionVars()); 349 for (auto symbolRef : attr->getAsRange<SymbolRefAttr>()) { 350 reductions.push_back( 351 SymbolTable::lookupNearestSymbolFrom<omp::ReductionDeclareOp>( 352 loop, symbolRef)); 353 } 354 } 355 356 /// Translates the blocks contained in the given region and appends them to at 357 /// the current insertion point of `builder`. The operations of the entry block 358 /// are appended to the current insertion block, which is not expected to have a 359 /// terminator. If set, `continuationBlockArgs` is populated with translated 360 /// values that correspond to the values omp.yield'ed from the region. 361 static LogicalResult inlineConvertOmpRegions( 362 Region ®ion, StringRef blockName, llvm::IRBuilderBase &builder, 363 LLVM::ModuleTranslation &moduleTranslation, 364 SmallVectorImpl<llvm::Value *> *continuationBlockArgs = nullptr) { 365 if (region.empty()) 366 return success(); 367 368 // Special case for single-block regions that don't create additional blocks: 369 // insert operations without creating additional blocks. 370 if (llvm::hasSingleElement(region)) { 371 moduleTranslation.mapBlock(®ion.front(), builder.GetInsertBlock()); 372 if (failed(moduleTranslation.convertBlock( 373 region.front(), /*ignoreArguments=*/true, builder))) 374 return failure(); 375 376 // The continuation arguments are simply the translated terminator operands. 377 if (continuationBlockArgs) 378 llvm::append_range( 379 *continuationBlockArgs, 380 moduleTranslation.lookupValues(region.front().back().getOperands())); 381 382 // Drop the mapping that is no longer necessary so that the same region can 383 // be processed multiple times. 384 moduleTranslation.forgetMapping(region); 385 return success(); 386 } 387 388 // Create the continuation block manually instead of calling splitBlock 389 // because the current insertion block may not have a terminator. 390 llvm::BasicBlock *continuationBlock = 391 llvm::BasicBlock::Create(builder.getContext(), blockName + ".cont", 392 builder.GetInsertBlock()->getParent(), 393 builder.GetInsertBlock()->getNextNode()); 394 builder.CreateBr(continuationBlock); 395 396 LogicalResult bodyGenStatus = success(); 397 SmallVector<llvm::PHINode *> phis; 398 convertOmpOpRegions(region, blockName, *builder.GetInsertBlock(), 399 *continuationBlock, builder, moduleTranslation, 400 bodyGenStatus, &phis); 401 if (failed(bodyGenStatus)) 402 return failure(); 403 if (continuationBlockArgs) 404 llvm::append_range(*continuationBlockArgs, phis); 405 builder.SetInsertPoint(continuationBlock, 406 continuationBlock->getFirstInsertionPt()); 407 return success(); 408 } 409 410 namespace { 411 /// Owning equivalents of OpenMPIRBuilder::(Atomic)ReductionGen that are used to 412 /// store lambdas with capture. 413 using OwningReductionGen = std::function<llvm::OpenMPIRBuilder::InsertPointTy( 414 llvm::OpenMPIRBuilder::InsertPointTy, llvm::Value *, llvm::Value *, 415 llvm::Value *&)>; 416 using OwningAtomicReductionGen = 417 std::function<llvm::OpenMPIRBuilder::InsertPointTy( 418 llvm::OpenMPIRBuilder::InsertPointTy, llvm::Type *, llvm::Value *, 419 llvm::Value *)>; 420 } // namespace 421 422 /// Create an OpenMPIRBuilder-compatible reduction generator for the given 423 /// reduction declaration. The generator uses `builder` but ignores its 424 /// insertion point. 425 static OwningReductionGen 426 makeReductionGen(omp::ReductionDeclareOp decl, llvm::IRBuilderBase &builder, 427 LLVM::ModuleTranslation &moduleTranslation) { 428 // The lambda is mutable because we need access to non-const methods of decl 429 // (which aren't actually mutating it), and we must capture decl by-value to 430 // avoid the dangling reference after the parent function returns. 431 OwningReductionGen gen = 432 [&, decl](llvm::OpenMPIRBuilder::InsertPointTy insertPoint, 433 llvm::Value *lhs, llvm::Value *rhs, 434 llvm::Value *&result) mutable { 435 Region &reductionRegion = decl.reductionRegion(); 436 moduleTranslation.mapValue(reductionRegion.front().getArgument(0), lhs); 437 moduleTranslation.mapValue(reductionRegion.front().getArgument(1), rhs); 438 builder.restoreIP(insertPoint); 439 SmallVector<llvm::Value *> phis; 440 if (failed(inlineConvertOmpRegions(reductionRegion, 441 "omp.reduction.nonatomic.body", 442 builder, moduleTranslation, &phis))) 443 return llvm::OpenMPIRBuilder::InsertPointTy(); 444 assert(phis.size() == 1); 445 result = phis[0]; 446 return builder.saveIP(); 447 }; 448 return gen; 449 } 450 451 /// Create an OpenMPIRBuilder-compatible atomic reduction generator for the 452 /// given reduction declaration. The generator uses `builder` but ignores its 453 /// insertion point. Returns null if there is no atomic region available in the 454 /// reduction declaration. 455 static OwningAtomicReductionGen 456 makeAtomicReductionGen(omp::ReductionDeclareOp decl, 457 llvm::IRBuilderBase &builder, 458 LLVM::ModuleTranslation &moduleTranslation) { 459 if (decl.atomicReductionRegion().empty()) 460 return OwningAtomicReductionGen(); 461 462 // The lambda is mutable because we need access to non-const methods of decl 463 // (which aren't actually mutating it), and we must capture decl by-value to 464 // avoid the dangling reference after the parent function returns. 465 OwningAtomicReductionGen atomicGen = 466 [&, decl](llvm::OpenMPIRBuilder::InsertPointTy insertPoint, llvm::Type *, 467 llvm::Value *lhs, llvm::Value *rhs) mutable { 468 Region &atomicRegion = decl.atomicReductionRegion(); 469 moduleTranslation.mapValue(atomicRegion.front().getArgument(0), lhs); 470 moduleTranslation.mapValue(atomicRegion.front().getArgument(1), rhs); 471 builder.restoreIP(insertPoint); 472 SmallVector<llvm::Value *> phis; 473 if (failed(inlineConvertOmpRegions(atomicRegion, 474 "omp.reduction.atomic.body", builder, 475 moduleTranslation, &phis))) 476 return llvm::OpenMPIRBuilder::InsertPointTy(); 477 assert(phis.empty()); 478 return builder.saveIP(); 479 }; 480 return atomicGen; 481 } 482 483 /// Converts an OpenMP 'ordered' operation into LLVM IR using OpenMPIRBuilder. 484 static LogicalResult 485 convertOmpOrdered(Operation &opInst, llvm::IRBuilderBase &builder, 486 LLVM::ModuleTranslation &moduleTranslation) { 487 auto orderedOp = cast<omp::OrderedOp>(opInst); 488 489 omp::ClauseDepend dependType = 490 *omp::symbolizeClauseDepend(orderedOp.depend_type_valAttr().getValue()); 491 bool isDependSource = dependType == omp::ClauseDepend::dependsource; 492 unsigned numLoops = orderedOp.num_loops_val().getValue(); 493 SmallVector<llvm::Value *> vecValues = 494 moduleTranslation.lookupValues(orderedOp.depend_vec_vars()); 495 496 llvm::OpenMPIRBuilder::LocationDescription ompLoc( 497 builder.saveIP(), builder.getCurrentDebugLocation()); 498 size_t indexVecValues = 0; 499 while (indexVecValues < vecValues.size()) { 500 SmallVector<llvm::Value *> storeValues; 501 storeValues.reserve(numLoops); 502 for (unsigned i = 0; i < numLoops; i++) { 503 storeValues.push_back(vecValues[indexVecValues]); 504 indexVecValues++; 505 } 506 builder.restoreIP(moduleTranslation.getOpenMPBuilder()->createOrderedDepend( 507 ompLoc, findAllocaInsertPoint(builder, moduleTranslation), numLoops, 508 storeValues, ".cnt.addr", isDependSource)); 509 } 510 return success(); 511 } 512 513 /// Converts an OpenMP 'ordered_region' operation into LLVM IR using 514 /// OpenMPIRBuilder. 515 static LogicalResult 516 convertOmpOrderedRegion(Operation &opInst, llvm::IRBuilderBase &builder, 517 LLVM::ModuleTranslation &moduleTranslation) { 518 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy; 519 auto orderedRegionOp = cast<omp::OrderedRegionOp>(opInst); 520 521 // TODO: The code generation for ordered simd directive is not supported yet. 522 if (orderedRegionOp.simd()) 523 return failure(); 524 525 // TODO: support error propagation in OpenMPIRBuilder and use it instead of 526 // relying on captured variables. 527 LogicalResult bodyGenStatus = success(); 528 529 auto bodyGenCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP, 530 llvm::BasicBlock &continuationBlock) { 531 // OrderedOp has only one region associated with it. 532 auto ®ion = cast<omp::OrderedRegionOp>(opInst).getRegion(); 533 convertOmpOpRegions(region, "omp.ordered.region", *codeGenIP.getBlock(), 534 continuationBlock, builder, moduleTranslation, 535 bodyGenStatus); 536 }; 537 538 // TODO: Perform finalization actions for variables. This has to be 539 // called for variables which have destructors/finalizers. 540 auto finiCB = [&](InsertPointTy codeGenIP) {}; 541 542 llvm::OpenMPIRBuilder::LocationDescription ompLoc( 543 builder.saveIP(), builder.getCurrentDebugLocation()); 544 builder.restoreIP( 545 moduleTranslation.getOpenMPBuilder()->createOrderedThreadsSimd( 546 ompLoc, bodyGenCB, finiCB, !orderedRegionOp.simd())); 547 return bodyGenStatus; 548 } 549 550 static LogicalResult 551 convertOmpSections(Operation &opInst, llvm::IRBuilderBase &builder, 552 LLVM::ModuleTranslation &moduleTranslation) { 553 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy; 554 using StorableBodyGenCallbackTy = 555 llvm::OpenMPIRBuilder::StorableBodyGenCallbackTy; 556 557 auto sectionsOp = cast<omp::SectionsOp>(opInst); 558 559 // TODO: Support the following clauses: private, firstprivate, lastprivate, 560 // reduction, allocate 561 if (!sectionsOp.private_vars().empty() || 562 !sectionsOp.firstprivate_vars().empty() || 563 !sectionsOp.lastprivate_vars().empty() || 564 !sectionsOp.reduction_vars().empty() || sectionsOp.reductions() || 565 !sectionsOp.allocate_vars().empty() || 566 !sectionsOp.allocators_vars().empty()) 567 return emitError(sectionsOp.getLoc()) 568 << "private, firstprivate, lastprivate, reduction and allocate " 569 "clauses are not supported for sections construct"; 570 571 LogicalResult bodyGenStatus = success(); 572 SmallVector<StorableBodyGenCallbackTy> sectionCBs; 573 574 for (Operation &op : *sectionsOp.region().begin()) { 575 auto sectionOp = dyn_cast<omp::SectionOp>(op); 576 if (!sectionOp) // omp.terminator 577 continue; 578 579 Region ®ion = sectionOp.region(); 580 auto sectionCB = [®ion, &builder, &moduleTranslation, &bodyGenStatus]( 581 InsertPointTy allocaIP, InsertPointTy codeGenIP, 582 llvm::BasicBlock &finiBB) { 583 builder.restoreIP(codeGenIP); 584 builder.CreateBr(&finiBB); 585 convertOmpOpRegions(region, "omp.section.region", *codeGenIP.getBlock(), 586 finiBB, builder, moduleTranslation, bodyGenStatus); 587 }; 588 sectionCBs.push_back(sectionCB); 589 } 590 591 // No sections within omp.sections operation - skip generation. This situation 592 // is only possible if there is only a terminator operation inside the 593 // sections operation 594 if (sectionCBs.empty()) 595 return success(); 596 597 assert(isa<omp::SectionOp>(*sectionsOp.region().op_begin())); 598 599 // TODO: Perform appropriate actions according to the data-sharing 600 // attribute (shared, private, firstprivate, ...) of variables. 601 // Currently defaults to shared. 602 auto privCB = [&](InsertPointTy, InsertPointTy codeGenIP, llvm::Value &, 603 llvm::Value &vPtr, 604 llvm::Value *&replacementValue) -> InsertPointTy { 605 replacementValue = &vPtr; 606 return codeGenIP; 607 }; 608 609 // TODO: Perform finalization actions for variables. This has to be 610 // called for variables which have destructors/finalizers. 611 auto finiCB = [&](InsertPointTy codeGenIP) {}; 612 613 llvm::OpenMPIRBuilder::LocationDescription ompLoc( 614 builder.saveIP(), builder.getCurrentDebugLocation()); 615 builder.restoreIP(moduleTranslation.getOpenMPBuilder()->createSections( 616 ompLoc, findAllocaInsertPoint(builder, moduleTranslation), sectionCBs, 617 privCB, finiCB, false, sectionsOp.nowait())); 618 return bodyGenStatus; 619 } 620 621 /// Converts an OpenMP workshare loop into LLVM IR using OpenMPIRBuilder. 622 static LogicalResult 623 convertOmpWsLoop(Operation &opInst, llvm::IRBuilderBase &builder, 624 LLVM::ModuleTranslation &moduleTranslation) { 625 auto loop = cast<omp::WsLoopOp>(opInst); 626 // TODO: this should be in the op verifier instead. 627 if (loop.lowerBound().empty()) 628 return failure(); 629 630 // Static is the default. 631 omp::ClauseScheduleKind schedule = omp::ClauseScheduleKind::Static; 632 if (loop.schedule_val().hasValue()) 633 schedule = 634 *omp::symbolizeClauseScheduleKind(loop.schedule_val().getValue()); 635 636 // Find the loop configuration. 637 llvm::Value *step = moduleTranslation.lookupValue(loop.step()[0]); 638 llvm::Type *ivType = step->getType(); 639 llvm::Value *chunk = 640 loop.schedule_chunk_var() 641 ? moduleTranslation.lookupValue(loop.schedule_chunk_var()) 642 : llvm::ConstantInt::get(ivType, 1); 643 644 SmallVector<omp::ReductionDeclareOp> reductionDecls; 645 collectReductionDecls(loop, reductionDecls); 646 llvm::OpenMPIRBuilder::InsertPointTy allocaIP = 647 findAllocaInsertPoint(builder, moduleTranslation); 648 649 // Allocate space for privatized reduction variables. 650 SmallVector<llvm::Value *> privateReductionVariables; 651 DenseMap<Value, llvm::Value *> reductionVariableMap; 652 unsigned numReductions = loop.getNumReductionVars(); 653 privateReductionVariables.reserve(numReductions); 654 if (numReductions != 0) { 655 llvm::IRBuilderBase::InsertPointGuard guard(builder); 656 builder.restoreIP(allocaIP); 657 for (unsigned i = 0; i < numReductions; ++i) { 658 auto reductionType = 659 loop.reduction_vars()[i].getType().cast<LLVM::LLVMPointerType>(); 660 llvm::Value *var = builder.CreateAlloca( 661 moduleTranslation.convertType(reductionType.getElementType())); 662 privateReductionVariables.push_back(var); 663 reductionVariableMap.try_emplace(loop.reduction_vars()[i], var); 664 } 665 } 666 667 // Store the mapping between reduction variables and their private copies on 668 // ModuleTranslation stack. It can be then recovered when translating 669 // omp.reduce operations in a separate call. 670 LLVM::ModuleTranslation::SaveStack<OpenMPVarMappingStackFrame> mappingGuard( 671 moduleTranslation, reductionVariableMap); 672 673 // Before the loop, store the initial values of reductions into reduction 674 // variables. Although this could be done after allocas, we don't want to mess 675 // up with the alloca insertion point. 676 for (unsigned i = 0; i < numReductions; ++i) { 677 SmallVector<llvm::Value *> phis; 678 if (failed(inlineConvertOmpRegions(reductionDecls[i].initializerRegion(), 679 "omp.reduction.neutral", builder, 680 moduleTranslation, &phis))) 681 return failure(); 682 assert(phis.size() == 1 && "expected one value to be yielded from the " 683 "reduction neutral element declaration region"); 684 builder.CreateStore(phis[0], privateReductionVariables[i]); 685 } 686 687 // Set up the source location value for OpenMP runtime. 688 llvm::DISubprogram *subprogram = 689 builder.GetInsertBlock()->getParent()->getSubprogram(); 690 const llvm::DILocation *diLoc = 691 moduleTranslation.translateLoc(opInst.getLoc(), subprogram); 692 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder.saveIP(), 693 llvm::DebugLoc(diLoc)); 694 695 // Generator of the canonical loop body. 696 // TODO: support error propagation in OpenMPIRBuilder and use it instead of 697 // relying on captured variables. 698 SmallVector<llvm::CanonicalLoopInfo *> loopInfos; 699 SmallVector<llvm::OpenMPIRBuilder::InsertPointTy> bodyInsertPoints; 700 LogicalResult bodyGenStatus = success(); 701 auto bodyGen = [&](llvm::OpenMPIRBuilder::InsertPointTy ip, llvm::Value *iv) { 702 // Make sure further conversions know about the induction variable. 703 moduleTranslation.mapValue( 704 loop.getRegion().front().getArgument(loopInfos.size()), iv); 705 706 // Capture the body insertion point for use in nested loops. BodyIP of the 707 // CanonicalLoopInfo always points to the beginning of the entry block of 708 // the body. 709 bodyInsertPoints.push_back(ip); 710 711 if (loopInfos.size() != loop.getNumLoops() - 1) 712 return; 713 714 // Convert the body of the loop. 715 llvm::BasicBlock *entryBlock = ip.getBlock(); 716 llvm::BasicBlock *exitBlock = 717 entryBlock->splitBasicBlock(ip.getPoint(), "omp.wsloop.exit"); 718 convertOmpOpRegions(loop.region(), "omp.wsloop.region", *entryBlock, 719 *exitBlock, builder, moduleTranslation, bodyGenStatus); 720 }; 721 722 // Delegate actual loop construction to the OpenMP IRBuilder. 723 // TODO: this currently assumes WsLoop is semantically similar to SCF loop, 724 // i.e. it has a positive step, uses signed integer semantics. Reconsider 725 // this code when WsLoop clearly supports more cases. 726 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder(); 727 for (unsigned i = 0, e = loop.getNumLoops(); i < e; ++i) { 728 llvm::Value *lowerBound = 729 moduleTranslation.lookupValue(loop.lowerBound()[i]); 730 llvm::Value *upperBound = 731 moduleTranslation.lookupValue(loop.upperBound()[i]); 732 llvm::Value *step = moduleTranslation.lookupValue(loop.step()[i]); 733 734 // Make sure loop trip count are emitted in the preheader of the outermost 735 // loop at the latest so that they are all available for the new collapsed 736 // loop will be created below. 737 llvm::OpenMPIRBuilder::LocationDescription loc = ompLoc; 738 llvm::OpenMPIRBuilder::InsertPointTy computeIP = ompLoc.IP; 739 if (i != 0) { 740 loc = llvm::OpenMPIRBuilder::LocationDescription(bodyInsertPoints.back(), 741 llvm::DebugLoc(diLoc)); 742 computeIP = loopInfos.front()->getPreheaderIP(); 743 } 744 loopInfos.push_back(ompBuilder->createCanonicalLoop( 745 loc, bodyGen, lowerBound, upperBound, step, 746 /*IsSigned=*/true, loop.inclusive(), computeIP)); 747 748 if (failed(bodyGenStatus)) 749 return failure(); 750 } 751 752 // Collapse loops. Store the insertion point because LoopInfos may get 753 // invalidated. 754 llvm::IRBuilderBase::InsertPoint afterIP = loopInfos.front()->getAfterIP(); 755 llvm::CanonicalLoopInfo *loopInfo = 756 ompBuilder->collapseLoops(diLoc, loopInfos, {}); 757 758 allocaIP = findAllocaInsertPoint(builder, moduleTranslation); 759 760 bool isSimd = loop.simd_modifier(); 761 762 if (schedule == omp::ClauseScheduleKind::Static) { 763 ompBuilder->applyStaticWorkshareLoop(ompLoc.DL, loopInfo, allocaIP, 764 !loop.nowait(), chunk); 765 } else { 766 llvm::omp::OMPScheduleType schedType; 767 switch (schedule) { 768 case omp::ClauseScheduleKind::Dynamic: 769 schedType = llvm::omp::OMPScheduleType::DynamicChunked; 770 break; 771 case omp::ClauseScheduleKind::Guided: 772 if (isSimd) 773 schedType = llvm::omp::OMPScheduleType::GuidedSimd; 774 else 775 schedType = llvm::omp::OMPScheduleType::GuidedChunked; 776 break; 777 case omp::ClauseScheduleKind::Auto: 778 schedType = llvm::omp::OMPScheduleType::Auto; 779 break; 780 case omp::ClauseScheduleKind::Runtime: 781 if (isSimd) 782 schedType = llvm::omp::OMPScheduleType::RuntimeSimd; 783 else 784 schedType = llvm::omp::OMPScheduleType::Runtime; 785 break; 786 default: 787 llvm_unreachable("Unknown schedule value"); 788 break; 789 } 790 791 if (loop.schedule_modifier().hasValue()) { 792 omp::ScheduleModifier modifier = 793 *omp::symbolizeScheduleModifier(loop.schedule_modifier().getValue()); 794 switch (modifier) { 795 case omp::ScheduleModifier::monotonic: 796 schedType |= llvm::omp::OMPScheduleType::ModifierMonotonic; 797 break; 798 case omp::ScheduleModifier::nonmonotonic: 799 schedType |= llvm::omp::OMPScheduleType::ModifierNonmonotonic; 800 break; 801 default: 802 // Nothing to do here. 803 break; 804 } 805 } 806 afterIP = ompBuilder->applyDynamicWorkshareLoop( 807 ompLoc.DL, loopInfo, allocaIP, schedType, !loop.nowait(), chunk); 808 } 809 810 // Continue building IR after the loop. Note that the LoopInfo returned by 811 // `collapseLoops` points inside the outermost loop and is intended for 812 // potential further loop transformations. Use the insertion point stored 813 // before collapsing loops instead. 814 builder.restoreIP(afterIP); 815 816 // Process the reductions if required. 817 if (numReductions == 0) 818 return success(); 819 820 // Create the reduction generators. We need to own them here because 821 // ReductionInfo only accepts references to the generators. 822 SmallVector<OwningReductionGen> owningReductionGens; 823 SmallVector<OwningAtomicReductionGen> owningAtomicReductionGens; 824 for (unsigned i = 0; i < numReductions; ++i) { 825 owningReductionGens.push_back( 826 makeReductionGen(reductionDecls[i], builder, moduleTranslation)); 827 owningAtomicReductionGens.push_back( 828 makeAtomicReductionGen(reductionDecls[i], builder, moduleTranslation)); 829 } 830 831 // Collect the reduction information. 832 SmallVector<llvm::OpenMPIRBuilder::ReductionInfo> reductionInfos; 833 reductionInfos.reserve(numReductions); 834 for (unsigned i = 0; i < numReductions; ++i) { 835 llvm::OpenMPIRBuilder::AtomicReductionGenTy atomicGen = nullptr; 836 if (owningAtomicReductionGens[i]) 837 atomicGen = owningAtomicReductionGens[i]; 838 llvm::Value *variable = 839 moduleTranslation.lookupValue(loop.reduction_vars()[i]); 840 reductionInfos.push_back({variable->getType()->getPointerElementType(), 841 variable, privateReductionVariables[i], 842 owningReductionGens[i], atomicGen}); 843 } 844 845 // The call to createReductions below expects the block to have a 846 // terminator. Create an unreachable instruction to serve as terminator 847 // and remove it later. 848 llvm::UnreachableInst *tempTerminator = builder.CreateUnreachable(); 849 builder.SetInsertPoint(tempTerminator); 850 llvm::OpenMPIRBuilder::InsertPointTy contInsertPoint = 851 ompBuilder->createReductions(builder.saveIP(), allocaIP, reductionInfos, 852 loop.nowait()); 853 if (!contInsertPoint.getBlock()) 854 return loop->emitOpError() << "failed to convert reductions"; 855 auto nextInsertionPoint = 856 ompBuilder->createBarrier(contInsertPoint, llvm::omp::OMPD_for); 857 tempTerminator->eraseFromParent(); 858 builder.restoreIP(nextInsertionPoint); 859 860 return success(); 861 } 862 863 // Convert an Atomic Ordering attribute to llvm::AtomicOrdering. 864 llvm::AtomicOrdering convertAtomicOrdering(Optional<StringRef> aoAttr) { 865 if (!aoAttr.hasValue()) 866 return llvm::AtomicOrdering::Monotonic; // Default Memory Ordering 867 868 return StringSwitch<llvm::AtomicOrdering>(aoAttr.getValue()) 869 .Case("seq_cst", llvm::AtomicOrdering::SequentiallyConsistent) 870 .Case("acq_rel", llvm::AtomicOrdering::AcquireRelease) 871 .Case("acquire", llvm::AtomicOrdering::Acquire) 872 .Case("release", llvm::AtomicOrdering::Release) 873 .Case("relaxed", llvm::AtomicOrdering::Monotonic) 874 .Default(llvm::AtomicOrdering::Monotonic); 875 } 876 877 // Convert omp.atomic.read operation to LLVM IR. 878 static LogicalResult 879 convertOmpAtomicRead(Operation &opInst, llvm::IRBuilderBase &builder, 880 LLVM::ModuleTranslation &moduleTranslation) { 881 882 auto readOp = cast<omp::AtomicReadOp>(opInst); 883 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder(); 884 885 // Set up the source location value for OpenMP runtime. 886 llvm::DISubprogram *subprogram = 887 builder.GetInsertBlock()->getParent()->getSubprogram(); 888 const llvm::DILocation *diLoc = 889 moduleTranslation.translateLoc(opInst.getLoc(), subprogram); 890 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder.saveIP(), 891 llvm::DebugLoc(diLoc)); 892 llvm::AtomicOrdering AO = convertAtomicOrdering(readOp.memory_order()); 893 llvm::Value *x = moduleTranslation.lookupValue(readOp.x()); 894 llvm::Value *v = moduleTranslation.lookupValue(readOp.v()); 895 llvm::OpenMPIRBuilder::AtomicOpValue V = {v, false, false}; 896 llvm::OpenMPIRBuilder::AtomicOpValue X = {x, false, false}; 897 builder.restoreIP(ompBuilder->createAtomicRead(ompLoc, X, V, AO)); 898 return success(); 899 } 900 901 /// Converts an omp.atomic.write operation to LLVM IR. 902 static LogicalResult 903 convertOmpAtomicWrite(Operation &opInst, llvm::IRBuilderBase &builder, 904 LLVM::ModuleTranslation &moduleTranslation) { 905 auto writeOp = cast<omp::AtomicWriteOp>(opInst); 906 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder(); 907 908 // Set up the source location value for OpenMP runtime. 909 llvm::DISubprogram *subprogram = 910 builder.GetInsertBlock()->getParent()->getSubprogram(); 911 const llvm::DILocation *diLoc = 912 moduleTranslation.translateLoc(opInst.getLoc(), subprogram); 913 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder.saveIP(), 914 llvm::DebugLoc(diLoc)); 915 llvm::AtomicOrdering ao = convertAtomicOrdering(writeOp.memory_order()); 916 llvm::Value *expr = moduleTranslation.lookupValue(writeOp.value()); 917 llvm::Value *dest = moduleTranslation.lookupValue(writeOp.address()); 918 llvm::OpenMPIRBuilder::AtomicOpValue x = {dest, /*isSigned=*/false, 919 /*isVolatile=*/false}; 920 builder.restoreIP(ompBuilder->createAtomicWrite(ompLoc, x, expr, ao)); 921 return success(); 922 } 923 924 /// Converts an OpenMP reduction operation using OpenMPIRBuilder. Expects the 925 /// mapping between reduction variables and their private equivalents to have 926 /// been stored on the ModuleTranslation stack. Currently only supports 927 /// reduction within WsLoopOp, but can be easily extended. 928 static LogicalResult 929 convertOmpReductionOp(omp::ReductionOp reductionOp, 930 llvm::IRBuilderBase &builder, 931 LLVM::ModuleTranslation &moduleTranslation) { 932 // Find the declaration that corresponds to the reduction op. 933 auto reductionContainer = reductionOp->getParentOfType<omp::WsLoopOp>(); 934 omp::ReductionDeclareOp declaration = 935 findReductionDecl(reductionContainer, reductionOp); 936 assert(declaration && "could not find reduction declaration"); 937 938 // Retrieve the mapping between reduction variables and their private 939 // equivalents. 940 const DenseMap<Value, llvm::Value *> *reductionVariableMap = nullptr; 941 moduleTranslation.stackWalk<OpenMPVarMappingStackFrame>( 942 [&](const OpenMPVarMappingStackFrame &frame) { 943 reductionVariableMap = &frame.mapping; 944 return WalkResult::interrupt(); 945 }); 946 assert(reductionVariableMap && "couldn't find private reduction variables"); 947 948 // Translate the reduction operation by emitting the body of the corresponding 949 // reduction declaration. 950 Region &reductionRegion = declaration.reductionRegion(); 951 llvm::Value *privateReductionVar = 952 reductionVariableMap->lookup(reductionOp.accumulator()); 953 llvm::Value *reductionVal = builder.CreateLoad( 954 moduleTranslation.convertType(reductionOp.operand().getType()), 955 privateReductionVar); 956 957 moduleTranslation.mapValue(reductionRegion.front().getArgument(0), 958 reductionVal); 959 moduleTranslation.mapValue( 960 reductionRegion.front().getArgument(1), 961 moduleTranslation.lookupValue(reductionOp.operand())); 962 963 SmallVector<llvm::Value *> phis; 964 if (failed(inlineConvertOmpRegions(reductionRegion, "omp.reduction.body", 965 builder, moduleTranslation, &phis))) 966 return failure(); 967 assert(phis.size() == 1 && "expected one value to be yielded from " 968 "the reduction body declaration region"); 969 builder.CreateStore(phis[0], privateReductionVar); 970 return success(); 971 } 972 973 namespace { 974 975 /// Implementation of the dialect interface that converts operations belonging 976 /// to the OpenMP dialect to LLVM IR. 977 class OpenMPDialectLLVMIRTranslationInterface 978 : public LLVMTranslationDialectInterface { 979 public: 980 using LLVMTranslationDialectInterface::LLVMTranslationDialectInterface; 981 982 /// Translates the given operation to LLVM IR using the provided IR builder 983 /// and saving the state in `moduleTranslation`. 984 LogicalResult 985 convertOperation(Operation *op, llvm::IRBuilderBase &builder, 986 LLVM::ModuleTranslation &moduleTranslation) const final; 987 }; 988 989 } // namespace 990 991 /// Given an OpenMP MLIR operation, create the corresponding LLVM IR 992 /// (including OpenMP runtime calls). 993 LogicalResult OpenMPDialectLLVMIRTranslationInterface::convertOperation( 994 Operation *op, llvm::IRBuilderBase &builder, 995 LLVM::ModuleTranslation &moduleTranslation) const { 996 997 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder(); 998 999 return llvm::TypeSwitch<Operation *, LogicalResult>(op) 1000 .Case([&](omp::BarrierOp) { 1001 ompBuilder->createBarrier(builder.saveIP(), llvm::omp::OMPD_barrier); 1002 return success(); 1003 }) 1004 .Case([&](omp::TaskwaitOp) { 1005 ompBuilder->createTaskwait(builder.saveIP()); 1006 return success(); 1007 }) 1008 .Case([&](omp::TaskyieldOp) { 1009 ompBuilder->createTaskyield(builder.saveIP()); 1010 return success(); 1011 }) 1012 .Case([&](omp::FlushOp) { 1013 // No support in Openmp runtime function (__kmpc_flush) to accept 1014 // the argument list. 1015 // OpenMP standard states the following: 1016 // "An implementation may implement a flush with a list by ignoring 1017 // the list, and treating it the same as a flush without a list." 1018 // 1019 // The argument list is discarded so that, flush with a list is treated 1020 // same as a flush without a list. 1021 ompBuilder->createFlush(builder.saveIP()); 1022 return success(); 1023 }) 1024 .Case([&](omp::ParallelOp) { 1025 return convertOmpParallel(*op, builder, moduleTranslation); 1026 }) 1027 .Case([&](omp::ReductionOp reductionOp) { 1028 return convertOmpReductionOp(reductionOp, builder, moduleTranslation); 1029 }) 1030 .Case([&](omp::MasterOp) { 1031 return convertOmpMaster(*op, builder, moduleTranslation); 1032 }) 1033 .Case([&](omp::CriticalOp) { 1034 return convertOmpCritical(*op, builder, moduleTranslation); 1035 }) 1036 .Case([&](omp::OrderedRegionOp) { 1037 return convertOmpOrderedRegion(*op, builder, moduleTranslation); 1038 }) 1039 .Case([&](omp::OrderedOp) { 1040 return convertOmpOrdered(*op, builder, moduleTranslation); 1041 }) 1042 .Case([&](omp::WsLoopOp) { 1043 return convertOmpWsLoop(*op, builder, moduleTranslation); 1044 }) 1045 .Case([&](omp::AtomicReadOp) { 1046 return convertOmpAtomicRead(*op, builder, moduleTranslation); 1047 }) 1048 .Case([&](omp::AtomicWriteOp) { 1049 return convertOmpAtomicWrite(*op, builder, moduleTranslation); 1050 }) 1051 .Case([&](omp::SectionsOp) { 1052 return convertOmpSections(*op, builder, moduleTranslation); 1053 }) 1054 .Case<omp::YieldOp, omp::TerminatorOp, omp::ReductionDeclareOp, 1055 omp::CriticalDeclareOp>([](auto op) { 1056 // `yield` and `terminator` can be just omitted. The block structure 1057 // was created in the region that handles their parent operation. 1058 // `reduction.declare` will be used by reductions and is not 1059 // converted directly, skip it. 1060 // `critical.declare` is only used to declare names of critical 1061 // sections which will be used by `critical` ops and hence can be 1062 // ignored for lowering. The OpenMP IRBuilder will create unique 1063 // name for critical section names. 1064 return success(); 1065 }) 1066 .Default([&](Operation *inst) { 1067 return inst->emitError("unsupported OpenMP operation: ") 1068 << inst->getName(); 1069 }); 1070 } 1071 1072 void mlir::registerOpenMPDialectTranslation(DialectRegistry ®istry) { 1073 registry.insert<omp::OpenMPDialect>(); 1074 registry.addDialectInterface<omp::OpenMPDialect, 1075 OpenMPDialectLLVMIRTranslationInterface>(); 1076 } 1077 1078 void mlir::registerOpenMPDialectTranslation(MLIRContext &context) { 1079 DialectRegistry registry; 1080 registerOpenMPDialectTranslation(registry); 1081 context.appendDialectRegistry(registry); 1082 } 1083