6859 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements a translation between the MLIR OpenMP dialect and LLVM10// IR.11//12//===----------------------------------------------------------------------===//13#include "mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"14#include "mlir/Analysis/TopologicalSortUtils.h"15#include "mlir/Dialect/LLVMIR/LLVMDialect.h"16#include "mlir/Dialect/LLVMIR/LLVMTypes.h"17#include "mlir/Dialect/OpenMP/OpenMPDialect.h"18#include "mlir/Dialect/OpenMP/OpenMPInterfaces.h"19#include "mlir/IR/Operation.h"20#include "mlir/Support/LLVM.h"21#include "mlir/Target/LLVMIR/Dialect/OpenMPCommon.h"22#include "mlir/Target/LLVMIR/ModuleTranslation.h"23 24#include "llvm/ADT/ArrayRef.h"25#include "llvm/ADT/SmallVector.h"26#include "llvm/ADT/TypeSwitch.h"27#include "llvm/Frontend/OpenMP/OMPConstants.h"28#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"29#include "llvm/IR/Constants.h"30#include "llvm/IR/DebugInfoMetadata.h"31#include "llvm/IR/DerivedTypes.h"32#include "llvm/IR/IRBuilder.h"33#include "llvm/IR/MDBuilder.h"34#include "llvm/IR/ReplaceConstant.h"35#include "llvm/Support/FileSystem.h"36#include "llvm/Support/VirtualFileSystem.h"37#include "llvm/TargetParser/Triple.h"38#include "llvm/Transforms/Utils/ModuleUtils.h"39 40#include <cstdint>41#include <iterator>42#include <numeric>43#include <optional>44#include <utility>45 46using namespace mlir;47 48namespace {49static llvm::omp::ScheduleKind50convertToScheduleKind(std::optional<omp::ClauseScheduleKind> schedKind) {51 if (!schedKind.has_value())52 return llvm::omp::OMP_SCHEDULE_Default;53 switch (schedKind.value()) {54 case omp::ClauseScheduleKind::Static:55 return llvm::omp::OMP_SCHEDULE_Static;56 case omp::ClauseScheduleKind::Dynamic:57 return llvm::omp::OMP_SCHEDULE_Dynamic;58 case omp::ClauseScheduleKind::Guided:59 return llvm::omp::OMP_SCHEDULE_Guided;60 case omp::ClauseScheduleKind::Auto:61 return llvm::omp::OMP_SCHEDULE_Auto;62 case omp::ClauseScheduleKind::Runtime:63 return llvm::omp::OMP_SCHEDULE_Runtime;64 case omp::ClauseScheduleKind::Distribute:65 return llvm::omp::OMP_SCHEDULE_Distribute;66 }67 llvm_unreachable("unhandled schedule clause argument");68}69 70/// ModuleTranslation stack frame for OpenMP operations. This keeps track of the71/// insertion points for allocas.72class OpenMPAllocaStackFrame73 : public StateStackFrameBase<OpenMPAllocaStackFrame> {74public:75 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OpenMPAllocaStackFrame)76 77 explicit OpenMPAllocaStackFrame(llvm::OpenMPIRBuilder::InsertPointTy allocaIP)78 : allocaInsertPoint(allocaIP) {}79 llvm::OpenMPIRBuilder::InsertPointTy allocaInsertPoint;80};81 82/// Stack frame to hold a \see llvm::CanonicalLoopInfo representing the83/// collapsed canonical loop information corresponding to an \c omp.loop_nest84/// operation.85class OpenMPLoopInfoStackFrame86 : public StateStackFrameBase<OpenMPLoopInfoStackFrame> {87public:88 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OpenMPLoopInfoStackFrame)89 llvm::CanonicalLoopInfo *loopInfo = nullptr;90};91 92/// Custom error class to signal translation errors that don't need reporting,93/// since encountering them will have already triggered relevant error messages.94///95/// Its purpose is to serve as the glue between MLIR failures represented as96/// \see LogicalResult instances and \see llvm::Error instances used to97/// propagate errors through the \see llvm::OpenMPIRBuilder. Generally, when an98/// error of the first type is raised, a message is emitted directly (the \see99/// LogicalResult itself does not hold any information). If we need to forward100/// this error condition as an \see llvm::Error while avoiding triggering some101/// redundant error reporting later on, we need a custom \see llvm::ErrorInfo102/// class to just signal this situation has happened.103///104/// For example, this class should be used to trigger errors from within105/// callbacks passed to the \see OpenMPIRBuilder when they were triggered by the106/// translation of their own regions. This unclutters the error log from107/// redundant messages.108class PreviouslyReportedError109 : public llvm::ErrorInfo<PreviouslyReportedError> {110public:111 void log(raw_ostream &) const override {112 // Do not log anything.113 }114 115 std::error_code convertToErrorCode() const override {116 llvm_unreachable(117 "PreviouslyReportedError doesn't support ECError conversion");118 }119 120 // Used by ErrorInfo::classID.121 static char ID;122};123 124char PreviouslyReportedError::ID = 0;125 126/*127 * Custom class for processing linear clause for omp.wsloop128 * and omp.simd. Linear clause translation requires setup,129 * initialization, update, and finalization at varying130 * basic blocks in the IR. This class helps maintain131 * internal state to allow consistent translation in132 * each of these stages.133 */134 135class LinearClauseProcessor {136 137private:138 SmallVector<llvm::Value *> linearPreconditionVars;139 SmallVector<llvm::Value *> linearLoopBodyTemps;140 SmallVector<llvm::AllocaInst *> linearOrigVars;141 SmallVector<llvm::Value *> linearOrigVal;142 SmallVector<llvm::Value *> linearSteps;143 llvm::BasicBlock *linearFinalizationBB;144 llvm::BasicBlock *linearExitBB;145 llvm::BasicBlock *linearLastIterExitBB;146 147public:148 // Allocate space for linear variabes149 void createLinearVar(llvm::IRBuilderBase &builder,150 LLVM::ModuleTranslation &moduleTranslation,151 mlir::Value &linearVar) {152 if (llvm::AllocaInst *linearVarAlloca = dyn_cast<llvm::AllocaInst>(153 moduleTranslation.lookupValue(linearVar))) {154 linearPreconditionVars.push_back(builder.CreateAlloca(155 linearVarAlloca->getAllocatedType(), nullptr, ".linear_var"));156 llvm::Value *linearLoopBodyTemp = builder.CreateAlloca(157 linearVarAlloca->getAllocatedType(), nullptr, ".linear_result");158 linearOrigVal.push_back(moduleTranslation.lookupValue(linearVar));159 linearLoopBodyTemps.push_back(linearLoopBodyTemp);160 linearOrigVars.push_back(linearVarAlloca);161 }162 }163 164 // Initialize linear step165 inline void initLinearStep(LLVM::ModuleTranslation &moduleTranslation,166 mlir::Value &linearStep) {167 linearSteps.push_back(moduleTranslation.lookupValue(linearStep));168 }169 170 // Emit IR for initialization of linear variables171 llvm::OpenMPIRBuilder::InsertPointOrErrorTy172 initLinearVar(llvm::IRBuilderBase &builder,173 LLVM::ModuleTranslation &moduleTranslation,174 llvm::BasicBlock *loopPreHeader) {175 builder.SetInsertPoint(loopPreHeader->getTerminator());176 for (size_t index = 0; index < linearOrigVars.size(); index++) {177 llvm::LoadInst *linearVarLoad = builder.CreateLoad(178 linearOrigVars[index]->getAllocatedType(), linearOrigVars[index]);179 builder.CreateStore(linearVarLoad, linearPreconditionVars[index]);180 }181 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterBarrierIP =182 moduleTranslation.getOpenMPBuilder()->createBarrier(183 builder.saveIP(), llvm::omp::OMPD_barrier);184 return afterBarrierIP;185 }186 187 // Emit IR for updating Linear variables188 void updateLinearVar(llvm::IRBuilderBase &builder, llvm::BasicBlock *loopBody,189 llvm::Value *loopInductionVar) {190 builder.SetInsertPoint(loopBody->getTerminator());191 for (size_t index = 0; index < linearPreconditionVars.size(); index++) {192 // Emit increments for linear vars193 llvm::LoadInst *linearVarStart =194 builder.CreateLoad(linearOrigVars[index]->getAllocatedType(),195 196 linearPreconditionVars[index]);197 auto mulInst = builder.CreateMul(loopInductionVar, linearSteps[index]);198 auto addInst = builder.CreateAdd(linearVarStart, mulInst);199 builder.CreateStore(addInst, linearLoopBodyTemps[index]);200 }201 }202 203 // Linear variable finalization is conditional on the last logical iteration.204 // Create BB splits to manage the same.205 void outlineLinearFinalizationBB(llvm::IRBuilderBase &builder,206 llvm::BasicBlock *loopExit) {207 linearFinalizationBB = loopExit->splitBasicBlock(208 loopExit->getTerminator(), "omp_loop.linear_finalization");209 linearExitBB = linearFinalizationBB->splitBasicBlock(210 linearFinalizationBB->getTerminator(), "omp_loop.linear_exit");211 linearLastIterExitBB = linearFinalizationBB->splitBasicBlock(212 linearFinalizationBB->getTerminator(), "omp_loop.linear_lastiter_exit");213 }214 215 // Finalize the linear vars216 llvm::OpenMPIRBuilder::InsertPointOrErrorTy217 finalizeLinearVar(llvm::IRBuilderBase &builder,218 LLVM::ModuleTranslation &moduleTranslation,219 llvm::Value *lastIter) {220 // Emit condition to check whether last logical iteration is being executed221 builder.SetInsertPoint(linearFinalizationBB->getTerminator());222 llvm::Value *loopLastIterLoad = builder.CreateLoad(223 llvm::Type::getInt32Ty(builder.getContext()), lastIter);224 llvm::Value *isLast =225 builder.CreateCmp(llvm::CmpInst::ICMP_NE, loopLastIterLoad,226 llvm::ConstantInt::get(227 llvm::Type::getInt32Ty(builder.getContext()), 0));228 // Store the linear variable values to original variables.229 builder.SetInsertPoint(linearLastIterExitBB->getTerminator());230 for (size_t index = 0; index < linearOrigVars.size(); index++) {231 llvm::LoadInst *linearVarTemp =232 builder.CreateLoad(linearOrigVars[index]->getAllocatedType(),233 linearLoopBodyTemps[index]);234 builder.CreateStore(linearVarTemp, linearOrigVars[index]);235 }236 237 // Create conditional branch such that the linear variable238 // values are stored to original variables only at the239 // last logical iteration240 builder.SetInsertPoint(linearFinalizationBB->getTerminator());241 builder.CreateCondBr(isLast, linearLastIterExitBB, linearExitBB);242 linearFinalizationBB->getTerminator()->eraseFromParent();243 // Emit barrier244 builder.SetInsertPoint(linearExitBB->getTerminator());245 return moduleTranslation.getOpenMPBuilder()->createBarrier(246 builder.saveIP(), llvm::omp::OMPD_barrier);247 }248 249 // Rewrite all uses of the original variable in `BBName`250 // with the linear variable in-place251 void rewriteInPlace(llvm::IRBuilderBase &builder, const std::string &BBName,252 size_t varIndex) {253 llvm::SmallVector<llvm::User *> users;254 for (llvm::User *user : linearOrigVal[varIndex]->users())255 users.push_back(user);256 for (auto *user : users) {257 if (auto *userInst = dyn_cast<llvm::Instruction>(user)) {258 if (userInst->getParent()->getName().str() == BBName)259 user->replaceUsesOfWith(linearOrigVal[varIndex],260 linearLoopBodyTemps[varIndex]);261 }262 }263 }264};265 266} // namespace267 268/// Looks up from the operation from and returns the PrivateClauseOp with269/// name symbolName270static omp::PrivateClauseOp findPrivatizer(Operation *from,271 SymbolRefAttr symbolName) {272 omp::PrivateClauseOp privatizer =273 SymbolTable::lookupNearestSymbolFrom<omp::PrivateClauseOp>(from,274 symbolName);275 assert(privatizer && "privatizer not found in the symbol table");276 return privatizer;277}278 279/// Check whether translation to LLVM IR for the given operation is currently280/// supported. If not, descriptive diagnostics will be emitted to let users know281/// this is a not-yet-implemented feature.282///283/// \returns success if no unimplemented features are needed to translate the284/// given operation.285static LogicalResult checkImplementationStatus(Operation &op) {286 auto todo = [&op](StringRef clauseName) {287 return op.emitError() << "not yet implemented: Unhandled clause "288 << clauseName << " in " << op.getName()289 << " operation";290 };291 292 auto checkAllocate = [&todo](auto op, LogicalResult &result) {293 if (!op.getAllocateVars().empty() || !op.getAllocatorVars().empty())294 result = todo("allocate");295 };296 auto checkBare = [&todo](auto op, LogicalResult &result) {297 if (op.getBare())298 result = todo("ompx_bare");299 };300 auto checkCancelDirective = [&todo](auto op, LogicalResult &result) {301 omp::ClauseCancellationConstructType cancelledDirective =302 op.getCancelDirective();303 // Cancelling a taskloop is not yet supported because we don't yet have LLVM304 // IR conversion for taskloop305 if (cancelledDirective == omp::ClauseCancellationConstructType::Taskgroup) {306 Operation *parent = op->getParentOp();307 while (parent) {308 if (parent->getDialect() == op->getDialect())309 break;310 parent = parent->getParentOp();311 }312 if (isa_and_nonnull<omp::TaskloopOp>(parent))313 result = todo("cancel directive inside of taskloop");314 }315 };316 auto checkDepend = [&todo](auto op, LogicalResult &result) {317 if (!op.getDependVars().empty() || op.getDependKinds())318 result = todo("depend");319 };320 auto checkDevice = [&todo](auto op, LogicalResult &result) {321 if (op.getDevice())322 result = todo("device");323 };324 auto checkHint = [](auto op, LogicalResult &) {325 if (op.getHint())326 op.emitWarning("hint clause discarded");327 };328 auto checkInReduction = [&todo](auto op, LogicalResult &result) {329 if (!op.getInReductionVars().empty() || op.getInReductionByref() ||330 op.getInReductionSyms())331 result = todo("in_reduction");332 };333 auto checkIsDevicePtr = [&todo](auto op, LogicalResult &result) {334 if (!op.getIsDevicePtrVars().empty())335 result = todo("is_device_ptr");336 };337 auto checkLinear = [&todo](auto op, LogicalResult &result) {338 if (!op.getLinearVars().empty() || !op.getLinearStepVars().empty())339 result = todo("linear");340 };341 auto checkNowait = [&todo](auto op, LogicalResult &result) {342 if (op.getNowait())343 result = todo("nowait");344 };345 auto checkOrder = [&todo](auto op, LogicalResult &result) {346 if (op.getOrder() || op.getOrderMod())347 result = todo("order");348 };349 auto checkParLevelSimd = [&todo](auto op, LogicalResult &result) {350 if (op.getParLevelSimd())351 result = todo("parallelization-level");352 };353 auto checkPriority = [&todo](auto op, LogicalResult &result) {354 if (op.getPriority())355 result = todo("priority");356 };357 auto checkPrivate = [&todo](auto op, LogicalResult &result) {358 if (!op.getPrivateVars().empty() || op.getPrivateSyms())359 result = todo("privatization");360 };361 auto checkReduction = [&todo](auto op, LogicalResult &result) {362 if (isa<omp::TeamsOp>(op))363 if (!op.getReductionVars().empty() || op.getReductionByref() ||364 op.getReductionSyms())365 result = todo("reduction");366 if (op.getReductionMod() &&367 op.getReductionMod().value() != omp::ReductionModifier::defaultmod)368 result = todo("reduction with modifier");369 };370 auto checkTaskReduction = [&todo](auto op, LogicalResult &result) {371 if (!op.getTaskReductionVars().empty() || op.getTaskReductionByref() ||372 op.getTaskReductionSyms())373 result = todo("task_reduction");374 };375 auto checkUntied = [&todo](auto op, LogicalResult &result) {376 if (op.getUntied())377 result = todo("untied");378 };379 380 LogicalResult result = success();381 llvm::TypeSwitch<Operation &>(op)382 .Case([&](omp::CancelOp op) { checkCancelDirective(op, result); })383 .Case([&](omp::CancellationPointOp op) {384 checkCancelDirective(op, result);385 })386 .Case([&](omp::DistributeOp op) {387 checkAllocate(op, result);388 checkOrder(op, result);389 })390 .Case([&](omp::OrderedRegionOp op) { checkParLevelSimd(op, result); })391 .Case([&](omp::SectionsOp op) {392 checkAllocate(op, result);393 checkPrivate(op, result);394 checkReduction(op, result);395 })396 .Case([&](omp::SingleOp op) {397 checkAllocate(op, result);398 checkPrivate(op, result);399 })400 .Case([&](omp::TeamsOp op) {401 checkAllocate(op, result);402 checkPrivate(op, result);403 })404 .Case([&](omp::TaskOp op) {405 checkAllocate(op, result);406 checkInReduction(op, result);407 })408 .Case([&](omp::TaskgroupOp op) {409 checkAllocate(op, result);410 checkTaskReduction(op, result);411 })412 .Case([&](omp::TaskwaitOp op) {413 checkDepend(op, result);414 checkNowait(op, result);415 })416 .Case([&](omp::TaskloopOp op) {417 // TODO: Add other clauses check418 checkUntied(op, result);419 checkPriority(op, result);420 })421 .Case([&](omp::WsloopOp op) {422 checkAllocate(op, result);423 checkLinear(op, result);424 checkOrder(op, result);425 checkReduction(op, result);426 })427 .Case([&](omp::ParallelOp op) {428 checkAllocate(op, result);429 checkReduction(op, result);430 })431 .Case([&](omp::SimdOp op) {432 checkLinear(op, result);433 checkReduction(op, result);434 })435 .Case<omp::AtomicReadOp, omp::AtomicWriteOp, omp::AtomicUpdateOp,436 omp::AtomicCaptureOp>([&](auto op) { checkHint(op, result); })437 .Case<omp::TargetEnterDataOp, omp::TargetExitDataOp, omp::TargetUpdateOp>(438 [&](auto op) { checkDepend(op, result); })439 .Case([&](omp::TargetOp op) {440 checkAllocate(op, result);441 checkBare(op, result);442 checkDevice(op, result);443 checkInReduction(op, result);444 checkIsDevicePtr(op, result);445 })446 .Default([](Operation &) {447 // Assume all clauses for an operation can be translated unless they are448 // checked above.449 });450 return result;451}452 453static LogicalResult handleError(llvm::Error error, Operation &op) {454 LogicalResult result = success();455 if (error) {456 llvm::handleAllErrors(457 std::move(error),458 [&](const PreviouslyReportedError &) { result = failure(); },459 [&](const llvm::ErrorInfoBase &err) {460 result = op.emitError(err.message());461 });462 }463 return result;464}465 466template <typename T>467static LogicalResult handleError(llvm::Expected<T> &result, Operation &op) {468 if (!result)469 return handleError(result.takeError(), op);470 471 return success();472}473 474/// Find the insertion point for allocas given the current insertion point for475/// normal operations in the builder.476static llvm::OpenMPIRBuilder::InsertPointTy477findAllocaInsertPoint(llvm::IRBuilderBase &builder,478 LLVM::ModuleTranslation &moduleTranslation) {479 // If there is an alloca insertion point on stack, i.e. we are in a nested480 // operation and a specific point was provided by some surrounding operation,481 // use it.482 llvm::OpenMPIRBuilder::InsertPointTy allocaInsertPoint;483 WalkResult walkResult = moduleTranslation.stackWalk<OpenMPAllocaStackFrame>(484 [&](OpenMPAllocaStackFrame &frame) {485 allocaInsertPoint = frame.allocaInsertPoint;486 return WalkResult::interrupt();487 });488 // In cases with multiple levels of outlining, the tree walk might find an489 // alloca insertion point that is inside the original function while the490 // builder insertion point is inside the outlined function. We need to make491 // sure that we do not use it in those cases.492 if (walkResult.wasInterrupted() &&493 allocaInsertPoint.getBlock()->getParent() ==494 builder.GetInsertBlock()->getParent())495 return allocaInsertPoint;496 497 // Otherwise, insert to the entry block of the surrounding function.498 // If the current IRBuilder InsertPoint is the function's entry, it cannot499 // also be used for alloca insertion which would result in insertion order500 // confusion. Create a new BasicBlock for the Builder and use the entry block501 // for the allocs.502 // TODO: Create a dedicated alloca BasicBlock at function creation such that503 // we do not need to move the current InertPoint here.504 if (builder.GetInsertBlock() ==505 &builder.GetInsertBlock()->getParent()->getEntryBlock()) {506 assert(builder.GetInsertPoint() == builder.GetInsertBlock()->end() &&507 "Assuming end of basic block");508 llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(509 builder.getContext(), "entry", builder.GetInsertBlock()->getParent(),510 builder.GetInsertBlock()->getNextNode());511 builder.CreateBr(entryBB);512 builder.SetInsertPoint(entryBB);513 }514 515 llvm::BasicBlock &funcEntryBlock =516 builder.GetInsertBlock()->getParent()->getEntryBlock();517 return llvm::OpenMPIRBuilder::InsertPointTy(518 &funcEntryBlock, funcEntryBlock.getFirstInsertionPt());519}520 521/// Find the loop information structure for the loop nest being translated. It522/// will return a `null` value unless called from the translation function for523/// a loop wrapper operation after successfully translating its body.524static llvm::CanonicalLoopInfo *525findCurrentLoopInfo(LLVM::ModuleTranslation &moduleTranslation) {526 llvm::CanonicalLoopInfo *loopInfo = nullptr;527 moduleTranslation.stackWalk<OpenMPLoopInfoStackFrame>(528 [&](OpenMPLoopInfoStackFrame &frame) {529 loopInfo = frame.loopInfo;530 return WalkResult::interrupt();531 });532 return loopInfo;533}534 535/// Converts the given region that appears within an OpenMP dialect operation to536/// LLVM IR, creating a branch from the `sourceBlock` to the entry block of the537/// region, and a branch from any block with an successor-less OpenMP terminator538/// to `continuationBlock`. Populates `continuationBlockPHIs` with the PHI nodes539/// of the continuation block if provided.540static llvm::Expected<llvm::BasicBlock *> convertOmpOpRegions(541 Region ®ion, StringRef blockName, llvm::IRBuilderBase &builder,542 LLVM::ModuleTranslation &moduleTranslation,543 SmallVectorImpl<llvm::PHINode *> *continuationBlockPHIs = nullptr) {544 bool isLoopWrapper = isa<omp::LoopWrapperInterface>(region.getParentOp());545 546 llvm::BasicBlock *continuationBlock =547 splitBB(builder, true, "omp.region.cont");548 llvm::BasicBlock *sourceBlock = builder.GetInsertBlock();549 550 llvm::LLVMContext &llvmContext = builder.getContext();551 for (Block &bb : region) {552 llvm::BasicBlock *llvmBB = llvm::BasicBlock::Create(553 llvmContext, blockName, builder.GetInsertBlock()->getParent(),554 builder.GetInsertBlock()->getNextNode());555 moduleTranslation.mapBlock(&bb, llvmBB);556 }557 558 llvm::Instruction *sourceTerminator = sourceBlock->getTerminator();559 560 // Terminators (namely YieldOp) may be forwarding values to the region that561 // need to be available in the continuation block. Collect the types of these562 // operands in preparation of creating PHI nodes. This is skipped for loop563 // wrapper operations, for which we know in advance they have no terminators.564 SmallVector<llvm::Type *> continuationBlockPHITypes;565 unsigned numYields = 0;566 567 if (!isLoopWrapper) {568 bool operandsProcessed = false;569 for (Block &bb : region.getBlocks()) {570 if (omp::YieldOp yield = dyn_cast<omp::YieldOp>(bb.getTerminator())) {571 if (!operandsProcessed) {572 for (unsigned i = 0, e = yield->getNumOperands(); i < e; ++i) {573 continuationBlockPHITypes.push_back(574 moduleTranslation.convertType(yield->getOperand(i).getType()));575 }576 operandsProcessed = true;577 } else {578 assert(continuationBlockPHITypes.size() == yield->getNumOperands() &&579 "mismatching number of values yielded from the region");580 for (unsigned i = 0, e = yield->getNumOperands(); i < e; ++i) {581 llvm::Type *operandType =582 moduleTranslation.convertType(yield->getOperand(i).getType());583 (void)operandType;584 assert(continuationBlockPHITypes[i] == operandType &&585 "values of mismatching types yielded from the region");586 }587 }588 numYields++;589 }590 }591 }592 593 // Insert PHI nodes in the continuation block for any values forwarded by the594 // terminators in this region.595 if (!continuationBlockPHITypes.empty())596 assert(597 continuationBlockPHIs &&598 "expected continuation block PHIs if converted regions yield values");599 if (continuationBlockPHIs) {600 llvm::IRBuilderBase::InsertPointGuard guard(builder);601 continuationBlockPHIs->reserve(continuationBlockPHITypes.size());602 builder.SetInsertPoint(continuationBlock, continuationBlock->begin());603 for (llvm::Type *ty : continuationBlockPHITypes)604 continuationBlockPHIs->push_back(builder.CreatePHI(ty, numYields));605 }606 607 // Convert blocks one by one in topological order to ensure608 // defs are converted before uses.609 SetVector<Block *> blocks = getBlocksSortedByDominance(region);610 for (Block *bb : blocks) {611 llvm::BasicBlock *llvmBB = moduleTranslation.lookupBlock(bb);612 // Retarget the branch of the entry block to the entry block of the613 // converted region (regions are single-entry).614 if (bb->isEntryBlock()) {615 assert(sourceTerminator->getNumSuccessors() == 1 &&616 "provided entry block has multiple successors");617 assert(sourceTerminator->getSuccessor(0) == continuationBlock &&618 "ContinuationBlock is not the successor of the entry block");619 sourceTerminator->setSuccessor(0, llvmBB);620 }621 622 llvm::IRBuilderBase::InsertPointGuard guard(builder);623 if (failed(624 moduleTranslation.convertBlock(*bb, bb->isEntryBlock(), builder)))625 return llvm::make_error<PreviouslyReportedError>();626 627 // Create a direct branch here for loop wrappers to prevent their lack of a628 // terminator from causing a crash below.629 if (isLoopWrapper) {630 builder.CreateBr(continuationBlock);631 continue;632 }633 634 // Special handling for `omp.yield` and `omp.terminator` (we may have more635 // than one): they return the control to the parent OpenMP dialect operation636 // so replace them with the branch to the continuation block. We handle this637 // here to avoid relying inter-function communication through the638 // ModuleTranslation class to set up the correct insertion point. This is639 // also consistent with MLIR's idiom of handling special region terminators640 // in the same code that handles the region-owning operation.641 Operation *terminator = bb->getTerminator();642 if (isa<omp::TerminatorOp, omp::YieldOp>(terminator)) {643 builder.CreateBr(continuationBlock);644 645 for (unsigned i = 0, e = terminator->getNumOperands(); i < e; ++i)646 (*continuationBlockPHIs)[i]->addIncoming(647 moduleTranslation.lookupValue(terminator->getOperand(i)), llvmBB);648 }649 }650 // After all blocks have been traversed and values mapped, connect the PHI651 // nodes to the results of preceding blocks.652 LLVM::detail::connectPHINodes(region, moduleTranslation);653 654 // Remove the blocks and values defined in this region from the mapping since655 // they are not visible outside of this region. This allows the same region to656 // be converted several times, that is cloned, without clashes, and slightly657 // speeds up the lookups.658 moduleTranslation.forgetMapping(region);659 660 return continuationBlock;661}662 663/// Convert ProcBindKind from MLIR-generated enum to LLVM enum.664static llvm::omp::ProcBindKind getProcBindKind(omp::ClauseProcBindKind kind) {665 switch (kind) {666 case omp::ClauseProcBindKind::Close:667 return llvm::omp::ProcBindKind::OMP_PROC_BIND_close;668 case omp::ClauseProcBindKind::Master:669 return llvm::omp::ProcBindKind::OMP_PROC_BIND_master;670 case omp::ClauseProcBindKind::Primary:671 return llvm::omp::ProcBindKind::OMP_PROC_BIND_primary;672 case omp::ClauseProcBindKind::Spread:673 return llvm::omp::ProcBindKind::OMP_PROC_BIND_spread;674 }675 llvm_unreachable("Unknown ClauseProcBindKind kind");676}677 678/// Maps block arguments from \p blockArgIface (which are MLIR values) to the679/// corresponding LLVM values of \p the interface's operands. This is useful680/// when an OpenMP region with entry block arguments is converted to LLVM. In681/// this case the block arguments are (part of) of the OpenMP region's entry682/// arguments and the operands are (part of) of the operands to the OpenMP op683/// containing the region.684static void forwardArgs(LLVM::ModuleTranslation &moduleTranslation,685 omp::BlockArgOpenMPOpInterface blockArgIface) {686 llvm::SmallVector<std::pair<Value, BlockArgument>> blockArgsPairs;687 blockArgIface.getBlockArgsPairs(blockArgsPairs);688 for (auto [var, arg] : blockArgsPairs)689 moduleTranslation.mapValue(arg, moduleTranslation.lookupValue(var));690}691 692/// Converts an OpenMP 'masked' operation into LLVM IR using OpenMPIRBuilder.693static LogicalResult694convertOmpMasked(Operation &opInst, llvm::IRBuilderBase &builder,695 LLVM::ModuleTranslation &moduleTranslation) {696 auto maskedOp = cast<omp::MaskedOp>(opInst);697 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;698 699 if (failed(checkImplementationStatus(opInst)))700 return failure();701 702 auto bodyGenCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP) {703 // MaskedOp has only one region associated with it.704 auto ®ion = maskedOp.getRegion();705 builder.restoreIP(codeGenIP);706 return convertOmpOpRegions(region, "omp.masked.region", builder,707 moduleTranslation)708 .takeError();709 };710 711 // TODO: Perform finalization actions for variables. This has to be712 // called for variables which have destructors/finalizers.713 auto finiCB = [&](InsertPointTy codeGenIP) { return llvm::Error::success(); };714 715 llvm::Value *filterVal = nullptr;716 if (auto filterVar = maskedOp.getFilteredThreadId()) {717 filterVal = moduleTranslation.lookupValue(filterVar);718 } else {719 llvm::LLVMContext &llvmContext = builder.getContext();720 filterVal =721 llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvmContext), /*V=*/0);722 }723 assert(filterVal != nullptr);724 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);725 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =726 moduleTranslation.getOpenMPBuilder()->createMasked(ompLoc, bodyGenCB,727 finiCB, filterVal);728 729 if (failed(handleError(afterIP, opInst)))730 return failure();731 732 builder.restoreIP(*afterIP);733 return success();734}735 736/// Converts an OpenMP 'master' operation into LLVM IR using OpenMPIRBuilder.737static LogicalResult738convertOmpMaster(Operation &opInst, llvm::IRBuilderBase &builder,739 LLVM::ModuleTranslation &moduleTranslation) {740 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;741 auto masterOp = cast<omp::MasterOp>(opInst);742 743 if (failed(checkImplementationStatus(opInst)))744 return failure();745 746 auto bodyGenCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP) {747 // MasterOp has only one region associated with it.748 auto ®ion = masterOp.getRegion();749 builder.restoreIP(codeGenIP);750 return convertOmpOpRegions(region, "omp.master.region", builder,751 moduleTranslation)752 .takeError();753 };754 755 // TODO: Perform finalization actions for variables. This has to be756 // called for variables which have destructors/finalizers.757 auto finiCB = [&](InsertPointTy codeGenIP) { return llvm::Error::success(); };758 759 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);760 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =761 moduleTranslation.getOpenMPBuilder()->createMaster(ompLoc, bodyGenCB,762 finiCB);763 764 if (failed(handleError(afterIP, opInst)))765 return failure();766 767 builder.restoreIP(*afterIP);768 return success();769}770 771/// Converts an OpenMP 'critical' operation into LLVM IR using OpenMPIRBuilder.772static LogicalResult773convertOmpCritical(Operation &opInst, llvm::IRBuilderBase &builder,774 LLVM::ModuleTranslation &moduleTranslation) {775 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;776 auto criticalOp = cast<omp::CriticalOp>(opInst);777 778 if (failed(checkImplementationStatus(opInst)))779 return failure();780 781 auto bodyGenCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP) {782 // CriticalOp has only one region associated with it.783 auto ®ion = cast<omp::CriticalOp>(opInst).getRegion();784 builder.restoreIP(codeGenIP);785 return convertOmpOpRegions(region, "omp.critical.region", builder,786 moduleTranslation)787 .takeError();788 };789 790 // TODO: Perform finalization actions for variables. This has to be791 // called for variables which have destructors/finalizers.792 auto finiCB = [&](InsertPointTy codeGenIP) { return llvm::Error::success(); };793 794 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);795 llvm::LLVMContext &llvmContext = moduleTranslation.getLLVMContext();796 llvm::Constant *hint = nullptr;797 798 // If it has a name, it probably has a hint too.799 if (criticalOp.getNameAttr()) {800 // The verifiers in OpenMP Dialect guarentee that all the pointers are801 // non-null802 auto symbolRef = cast<SymbolRefAttr>(criticalOp.getNameAttr());803 auto criticalDeclareOp =804 SymbolTable::lookupNearestSymbolFrom<omp::CriticalDeclareOp>(criticalOp,805 symbolRef);806 hint =807 llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvmContext),808 static_cast<int>(criticalDeclareOp.getHint()));809 }810 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =811 moduleTranslation.getOpenMPBuilder()->createCritical(812 ompLoc, bodyGenCB, finiCB, criticalOp.getName().value_or(""), hint);813 814 if (failed(handleError(afterIP, opInst)))815 return failure();816 817 builder.restoreIP(*afterIP);818 return success();819}820 821/// A util to collect info needed to convert delayed privatizers from MLIR to822/// LLVM.823struct PrivateVarsInfo {824 template <typename OP>825 PrivateVarsInfo(OP op)826 : blockArgs(827 cast<omp::BlockArgOpenMPOpInterface>(*op).getPrivateBlockArgs()) {828 mlirVars.reserve(blockArgs.size());829 llvmVars.reserve(blockArgs.size());830 collectPrivatizationDecls<OP>(op);831 832 for (mlir::Value privateVar : op.getPrivateVars())833 mlirVars.push_back(privateVar);834 }835 836 MutableArrayRef<BlockArgument> blockArgs;837 SmallVector<mlir::Value> mlirVars;838 SmallVector<llvm::Value *> llvmVars;839 SmallVector<omp::PrivateClauseOp> privatizers;840 841private:842 /// Populates `privatizations` with privatization declarations used for the843 /// given op.844 template <class OP>845 void collectPrivatizationDecls(OP op) {846 std::optional<ArrayAttr> attr = op.getPrivateSyms();847 if (!attr)848 return;849 850 privatizers.reserve(privatizers.size() + attr->size());851 for (auto symbolRef : attr->getAsRange<SymbolRefAttr>()) {852 privatizers.push_back(findPrivatizer(op, symbolRef));853 }854 }855};856 857/// Populates `reductions` with reduction declarations used in the given op.858template <typename T>859static void860collectReductionDecls(T op,861 SmallVectorImpl<omp::DeclareReductionOp> &reductions) {862 std::optional<ArrayAttr> attr = op.getReductionSyms();863 if (!attr)864 return;865 866 reductions.reserve(reductions.size() + op.getNumReductionVars());867 for (auto symbolRef : attr->getAsRange<SymbolRefAttr>()) {868 reductions.push_back(869 SymbolTable::lookupNearestSymbolFrom<omp::DeclareReductionOp>(870 op, symbolRef));871 }872}873 874/// Translates the blocks contained in the given region and appends them to at875/// the current insertion point of `builder`. The operations of the entry block876/// are appended to the current insertion block. If set, `continuationBlockArgs`877/// is populated with translated values that correspond to the values878/// omp.yield'ed from the region.879static LogicalResult inlineConvertOmpRegions(880 Region ®ion, StringRef blockName, llvm::IRBuilderBase &builder,881 LLVM::ModuleTranslation &moduleTranslation,882 SmallVectorImpl<llvm::Value *> *continuationBlockArgs = nullptr) {883 if (region.empty())884 return success();885 886 // Special case for single-block regions that don't create additional blocks:887 // insert operations without creating additional blocks.888 if (region.hasOneBlock()) {889 llvm::Instruction *potentialTerminator =890 builder.GetInsertBlock()->empty() ? nullptr891 : &builder.GetInsertBlock()->back();892 893 if (potentialTerminator && potentialTerminator->isTerminator())894 potentialTerminator->removeFromParent();895 moduleTranslation.mapBlock(®ion.front(), builder.GetInsertBlock());896 897 if (failed(moduleTranslation.convertBlock(898 region.front(), /*ignoreArguments=*/true, builder)))899 return failure();900 901 // The continuation arguments are simply the translated terminator operands.902 if (continuationBlockArgs)903 llvm::append_range(904 *continuationBlockArgs,905 moduleTranslation.lookupValues(region.front().back().getOperands()));906 907 // Drop the mapping that is no longer necessary so that the same region can908 // be processed multiple times.909 moduleTranslation.forgetMapping(region);910 911 if (potentialTerminator && potentialTerminator->isTerminator()) {912 llvm::BasicBlock *block = builder.GetInsertBlock();913 if (block->empty()) {914 // this can happen for really simple reduction init regions e.g.915 // %0 = llvm.mlir.constant(0 : i32) : i32916 // omp.yield(%0 : i32)917 // because the llvm.mlir.constant (MLIR op) isn't converted into any918 // llvm op919 potentialTerminator->insertInto(block, block->begin());920 } else {921 potentialTerminator->insertAfter(&block->back());922 }923 }924 925 return success();926 }927 928 SmallVector<llvm::PHINode *> phis;929 llvm::Expected<llvm::BasicBlock *> continuationBlock =930 convertOmpOpRegions(region, blockName, builder, moduleTranslation, &phis);931 932 if (failed(handleError(continuationBlock, *region.getParentOp())))933 return failure();934 935 if (continuationBlockArgs)936 llvm::append_range(*continuationBlockArgs, phis);937 builder.SetInsertPoint(*continuationBlock,938 (*continuationBlock)->getFirstInsertionPt());939 return success();940}941 942namespace {943/// Owning equivalents of OpenMPIRBuilder::(Atomic)ReductionGen that are used to944/// store lambdas with capture.945using OwningReductionGen =946 std::function<llvm::OpenMPIRBuilder::InsertPointOrErrorTy(947 llvm::OpenMPIRBuilder::InsertPointTy, llvm::Value *, llvm::Value *,948 llvm::Value *&)>;949using OwningAtomicReductionGen =950 std::function<llvm::OpenMPIRBuilder::InsertPointOrErrorTy(951 llvm::OpenMPIRBuilder::InsertPointTy, llvm::Type *, llvm::Value *,952 llvm::Value *)>;953using OwningDataPtrPtrReductionGen =954 std::function<llvm::OpenMPIRBuilder::InsertPointOrErrorTy(955 llvm::OpenMPIRBuilder::InsertPointTy, llvm::Value *, llvm::Value *&)>;956} // namespace957 958/// Create an OpenMPIRBuilder-compatible reduction generator for the given959/// reduction declaration. The generator uses `builder` but ignores its960/// insertion point.961static OwningReductionGen962makeReductionGen(omp::DeclareReductionOp decl, llvm::IRBuilderBase &builder,963 LLVM::ModuleTranslation &moduleTranslation) {964 // The lambda is mutable because we need access to non-const methods of decl965 // (which aren't actually mutating it), and we must capture decl by-value to966 // avoid the dangling reference after the parent function returns.967 OwningReductionGen gen =968 [&, decl](llvm::OpenMPIRBuilder::InsertPointTy insertPoint,969 llvm::Value *lhs, llvm::Value *rhs,970 llvm::Value *&result) mutable971 -> llvm::OpenMPIRBuilder::InsertPointOrErrorTy {972 moduleTranslation.mapValue(decl.getReductionLhsArg(), lhs);973 moduleTranslation.mapValue(decl.getReductionRhsArg(), rhs);974 builder.restoreIP(insertPoint);975 SmallVector<llvm::Value *> phis;976 if (failed(inlineConvertOmpRegions(decl.getReductionRegion(),977 "omp.reduction.nonatomic.body", builder,978 moduleTranslation, &phis)))979 return llvm::createStringError(980 "failed to inline `combiner` region of `omp.declare_reduction`");981 result = llvm::getSingleElement(phis);982 return builder.saveIP();983 };984 return gen;985}986 987/// Create an OpenMPIRBuilder-compatible atomic reduction generator for the988/// given reduction declaration. The generator uses `builder` but ignores its989/// insertion point. Returns null if there is no atomic region available in the990/// reduction declaration.991static OwningAtomicReductionGen992makeAtomicReductionGen(omp::DeclareReductionOp decl,993 llvm::IRBuilderBase &builder,994 LLVM::ModuleTranslation &moduleTranslation) {995 if (decl.getAtomicReductionRegion().empty())996 return OwningAtomicReductionGen();997 998 // The lambda is mutable because we need access to non-const methods of decl999 // (which aren't actually mutating it), and we must capture decl by-value to1000 // avoid the dangling reference after the parent function returns.1001 OwningAtomicReductionGen atomicGen =1002 [&, decl](llvm::OpenMPIRBuilder::InsertPointTy insertPoint, llvm::Type *,1003 llvm::Value *lhs, llvm::Value *rhs) mutable1004 -> llvm::OpenMPIRBuilder::InsertPointOrErrorTy {1005 moduleTranslation.mapValue(decl.getAtomicReductionLhsArg(), lhs);1006 moduleTranslation.mapValue(decl.getAtomicReductionRhsArg(), rhs);1007 builder.restoreIP(insertPoint);1008 SmallVector<llvm::Value *> phis;1009 if (failed(inlineConvertOmpRegions(decl.getAtomicReductionRegion(),1010 "omp.reduction.atomic.body", builder,1011 moduleTranslation, &phis)))1012 return llvm::createStringError(1013 "failed to inline `atomic` region of `omp.declare_reduction`");1014 assert(phis.empty());1015 return builder.saveIP();1016 };1017 return atomicGen;1018}1019 1020/// Create an OpenMPIRBuilder-compatible `data_ptr_ptr` reduction generator for1021/// the given reduction declaration. The generator uses `builder` but ignores1022/// its insertion point. Returns null if there is no `data_ptr_ptr` region1023/// available in the reduction declaration.1024static OwningDataPtrPtrReductionGen1025makeRefDataPtrGen(omp::DeclareReductionOp decl, llvm::IRBuilderBase &builder,1026 LLVM::ModuleTranslation &moduleTranslation, bool isByRef) {1027 if (!isByRef)1028 return OwningDataPtrPtrReductionGen();1029 1030 OwningDataPtrPtrReductionGen refDataPtrGen =1031 [&, decl](llvm::OpenMPIRBuilder::InsertPointTy insertPoint,1032 llvm::Value *byRefVal, llvm::Value *&result) mutable1033 -> llvm::OpenMPIRBuilder::InsertPointOrErrorTy {1034 moduleTranslation.mapValue(decl.getDataPtrPtrRegionArg(), byRefVal);1035 builder.restoreIP(insertPoint);1036 SmallVector<llvm::Value *> phis;1037 if (failed(inlineConvertOmpRegions(decl.getDataPtrPtrRegion(),1038 "omp.data_ptr_ptr.body", builder,1039 moduleTranslation, &phis)))1040 return llvm::createStringError(1041 "failed to inline `data_ptr_ptr` region of `omp.declare_reduction`");1042 result = llvm::getSingleElement(phis);1043 return builder.saveIP();1044 };1045 1046 return refDataPtrGen;1047}1048 1049/// Converts an OpenMP 'ordered' operation into LLVM IR using OpenMPIRBuilder.1050static LogicalResult1051convertOmpOrdered(Operation &opInst, llvm::IRBuilderBase &builder,1052 LLVM::ModuleTranslation &moduleTranslation) {1053 auto orderedOp = cast<omp::OrderedOp>(opInst);1054 1055 if (failed(checkImplementationStatus(opInst)))1056 return failure();1057 1058 omp::ClauseDepend dependType = *orderedOp.getDoacrossDependType();1059 bool isDependSource = dependType == omp::ClauseDepend::dependsource;1060 unsigned numLoops = *orderedOp.getDoacrossNumLoops();1061 SmallVector<llvm::Value *> vecValues =1062 moduleTranslation.lookupValues(orderedOp.getDoacrossDependVars());1063 1064 size_t indexVecValues = 0;1065 while (indexVecValues < vecValues.size()) {1066 SmallVector<llvm::Value *> storeValues;1067 storeValues.reserve(numLoops);1068 for (unsigned i = 0; i < numLoops; i++) {1069 storeValues.push_back(vecValues[indexVecValues]);1070 indexVecValues++;1071 }1072 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =1073 findAllocaInsertPoint(builder, moduleTranslation);1074 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);1075 builder.restoreIP(moduleTranslation.getOpenMPBuilder()->createOrderedDepend(1076 ompLoc, allocaIP, numLoops, storeValues, ".cnt.addr", isDependSource));1077 }1078 return success();1079}1080 1081/// Converts an OpenMP 'ordered_region' operation into LLVM IR using1082/// OpenMPIRBuilder.1083static LogicalResult1084convertOmpOrderedRegion(Operation &opInst, llvm::IRBuilderBase &builder,1085 LLVM::ModuleTranslation &moduleTranslation) {1086 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;1087 auto orderedRegionOp = cast<omp::OrderedRegionOp>(opInst);1088 1089 if (failed(checkImplementationStatus(opInst)))1090 return failure();1091 1092 auto bodyGenCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP) {1093 // OrderedOp has only one region associated with it.1094 auto ®ion = cast<omp::OrderedRegionOp>(opInst).getRegion();1095 builder.restoreIP(codeGenIP);1096 return convertOmpOpRegions(region, "omp.ordered.region", builder,1097 moduleTranslation)1098 .takeError();1099 };1100 1101 // TODO: Perform finalization actions for variables. This has to be1102 // called for variables which have destructors/finalizers.1103 auto finiCB = [&](InsertPointTy codeGenIP) { return llvm::Error::success(); };1104 1105 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);1106 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =1107 moduleTranslation.getOpenMPBuilder()->createOrderedThreadsSimd(1108 ompLoc, bodyGenCB, finiCB, !orderedRegionOp.getParLevelSimd());1109 1110 if (failed(handleError(afterIP, opInst)))1111 return failure();1112 1113 builder.restoreIP(*afterIP);1114 return success();1115}1116 1117namespace {1118/// Contains the arguments for an LLVM store operation1119struct DeferredStore {1120 DeferredStore(llvm::Value *value, llvm::Value *address)1121 : value(value), address(address) {}1122 1123 llvm::Value *value;1124 llvm::Value *address;1125};1126} // namespace1127 1128/// Allocate space for privatized reduction variables.1129/// `deferredStores` contains information to create store operations which needs1130/// to be inserted after all allocas1131template <typename T>1132static LogicalResult1133allocReductionVars(T loop, ArrayRef<BlockArgument> reductionArgs,1134 llvm::IRBuilderBase &builder,1135 LLVM::ModuleTranslation &moduleTranslation,1136 const llvm::OpenMPIRBuilder::InsertPointTy &allocaIP,1137 SmallVectorImpl<omp::DeclareReductionOp> &reductionDecls,1138 SmallVectorImpl<llvm::Value *> &privateReductionVariables,1139 DenseMap<Value, llvm::Value *> &reductionVariableMap,1140 SmallVectorImpl<DeferredStore> &deferredStores,1141 llvm::ArrayRef<bool> isByRefs) {1142 llvm::IRBuilderBase::InsertPointGuard guard(builder);1143 builder.SetInsertPoint(allocaIP.getBlock()->getTerminator());1144 1145 // delay creating stores until after all allocas1146 deferredStores.reserve(loop.getNumReductionVars());1147 1148 for (std::size_t i = 0; i < loop.getNumReductionVars(); ++i) {1149 Region &allocRegion = reductionDecls[i].getAllocRegion();1150 if (isByRefs[i]) {1151 if (allocRegion.empty())1152 continue;1153 1154 SmallVector<llvm::Value *, 1> phis;1155 if (failed(inlineConvertOmpRegions(allocRegion, "omp.reduction.alloc",1156 builder, moduleTranslation, &phis)))1157 return loop.emitError(1158 "failed to inline `alloc` region of `omp.declare_reduction`");1159 1160 assert(phis.size() == 1 && "expected one allocation to be yielded");1161 builder.SetInsertPoint(allocaIP.getBlock()->getTerminator());1162 1163 // Allocate reduction variable (which is a pointer to the real reduction1164 // variable allocated in the inlined region)1165 llvm::Value *var = builder.CreateAlloca(1166 moduleTranslation.convertType(reductionDecls[i].getType()));1167 1168 llvm::Type *ptrTy = builder.getPtrTy();1169 llvm::Value *castVar =1170 builder.CreatePointerBitCastOrAddrSpaceCast(var, ptrTy);1171 llvm::Value *castPhi =1172 builder.CreatePointerBitCastOrAddrSpaceCast(phis[0], ptrTy);1173 1174 deferredStores.emplace_back(castPhi, castVar);1175 1176 privateReductionVariables[i] = castVar;1177 moduleTranslation.mapValue(reductionArgs[i], castPhi);1178 reductionVariableMap.try_emplace(loop.getReductionVars()[i], castPhi);1179 } else {1180 assert(allocRegion.empty() &&1181 "allocaction is implicit for by-val reduction");1182 llvm::Value *var = builder.CreateAlloca(1183 moduleTranslation.convertType(reductionDecls[i].getType()));1184 1185 llvm::Type *ptrTy = builder.getPtrTy();1186 llvm::Value *castVar =1187 builder.CreatePointerBitCastOrAddrSpaceCast(var, ptrTy);1188 1189 moduleTranslation.mapValue(reductionArgs[i], castVar);1190 privateReductionVariables[i] = castVar;1191 reductionVariableMap.try_emplace(loop.getReductionVars()[i], castVar);1192 }1193 }1194 1195 return success();1196}1197 1198/// Map input arguments to reduction initialization region1199template <typename T>1200static void1201mapInitializationArgs(T loop, LLVM::ModuleTranslation &moduleTranslation,1202 llvm::IRBuilderBase &builder,1203 SmallVectorImpl<omp::DeclareReductionOp> &reductionDecls,1204 DenseMap<Value, llvm::Value *> &reductionVariableMap,1205 unsigned i) {1206 // map input argument to the initialization region1207 mlir::omp::DeclareReductionOp &reduction = reductionDecls[i];1208 Region &initializerRegion = reduction.getInitializerRegion();1209 Block &entry = initializerRegion.front();1210 1211 mlir::Value mlirSource = loop.getReductionVars()[i];1212 llvm::Value *llvmSource = moduleTranslation.lookupValue(mlirSource);1213 llvm::Value *origVal = llvmSource;1214 // If a non-pointer value is expected, load the value from the source pointer.1215 if (!isa<LLVM::LLVMPointerType>(1216 reduction.getInitializerMoldArg().getType()) &&1217 isa<LLVM::LLVMPointerType>(mlirSource.getType())) {1218 origVal =1219 builder.CreateLoad(moduleTranslation.convertType(1220 reduction.getInitializerMoldArg().getType()),1221 llvmSource, "omp_orig");1222 }1223 moduleTranslation.mapValue(reduction.getInitializerMoldArg(), origVal);1224 1225 if (entry.getNumArguments() > 1) {1226 llvm::Value *allocation =1227 reductionVariableMap.lookup(loop.getReductionVars()[i]);1228 moduleTranslation.mapValue(reduction.getInitializerAllocArg(), allocation);1229 }1230}1231 1232static void1233setInsertPointForPossiblyEmptyBlock(llvm::IRBuilderBase &builder,1234 llvm::BasicBlock *block = nullptr) {1235 if (block == nullptr)1236 block = builder.GetInsertBlock();1237 1238 if (block->empty() || block->getTerminator() == nullptr)1239 builder.SetInsertPoint(block);1240 else1241 builder.SetInsertPoint(block->getTerminator());1242}1243 1244/// Inline reductions' `init` regions. This functions assumes that the1245/// `builder`'s insertion point is where the user wants the `init` regions to be1246/// inlined; i.e. it does not try to find a proper insertion location for the1247/// `init` regions. It also leaves the `builder's insertions point in a state1248/// where the user can continue the code-gen directly afterwards.1249template <typename OP>1250static LogicalResult1251initReductionVars(OP op, ArrayRef<BlockArgument> reductionArgs,1252 llvm::IRBuilderBase &builder,1253 LLVM::ModuleTranslation &moduleTranslation,1254 llvm::BasicBlock *latestAllocaBlock,1255 SmallVectorImpl<omp::DeclareReductionOp> &reductionDecls,1256 SmallVectorImpl<llvm::Value *> &privateReductionVariables,1257 DenseMap<Value, llvm::Value *> &reductionVariableMap,1258 llvm::ArrayRef<bool> isByRef,1259 SmallVectorImpl<DeferredStore> &deferredStores) {1260 if (op.getNumReductionVars() == 0)1261 return success();1262 1263 llvm::BasicBlock *initBlock = splitBB(builder, true, "omp.reduction.init");1264 auto allocaIP = llvm::IRBuilderBase::InsertPoint(1265 latestAllocaBlock, latestAllocaBlock->getTerminator()->getIterator());1266 builder.restoreIP(allocaIP);1267 SmallVector<llvm::Value *> byRefVars(op.getNumReductionVars());1268 1269 for (unsigned i = 0; i < op.getNumReductionVars(); ++i) {1270 if (isByRef[i]) {1271 if (!reductionDecls[i].getAllocRegion().empty())1272 continue;1273 1274 // TODO: remove after all users of by-ref are updated to use the alloc1275 // region: Allocate reduction variable (which is a pointer to the real1276 // reduciton variable allocated in the inlined region)1277 byRefVars[i] = builder.CreateAlloca(1278 moduleTranslation.convertType(reductionDecls[i].getType()));1279 }1280 }1281 1282 setInsertPointForPossiblyEmptyBlock(builder, initBlock);1283 1284 // store result of the alloc region to the allocated pointer to the real1285 // reduction variable1286 for (auto [data, addr] : deferredStores)1287 builder.CreateStore(data, addr);1288 1289 // Before the loop, store the initial values of reductions into reduction1290 // variables. Although this could be done after allocas, we don't want to mess1291 // up with the alloca insertion point.1292 for (unsigned i = 0; i < op.getNumReductionVars(); ++i) {1293 SmallVector<llvm::Value *, 1> phis;1294 1295 // map block argument to initializer region1296 mapInitializationArgs(op, moduleTranslation, builder, reductionDecls,1297 reductionVariableMap, i);1298 1299 // TODO In some cases (specially on the GPU), the init regions may1300 // contains stack alloctaions. If the region is inlined in a loop, this is1301 // problematic. Instead of just inlining the region, handle allocations by1302 // hoisting fixed length allocations to the function entry and using1303 // stacksave and restore for variable length ones.1304 if (failed(inlineConvertOmpRegions(reductionDecls[i].getInitializerRegion(),1305 "omp.reduction.neutral", builder,1306 moduleTranslation, &phis)))1307 return failure();1308 1309 assert(phis.size() == 1 && "expected one value to be yielded from the "1310 "reduction neutral element declaration region");1311 1312 setInsertPointForPossiblyEmptyBlock(builder);1313 1314 if (isByRef[i]) {1315 if (!reductionDecls[i].getAllocRegion().empty())1316 // done in allocReductionVars1317 continue;1318 1319 // TODO: this path can be removed once all users of by-ref are updated to1320 // use an alloc region1321 1322 // Store the result of the inlined region to the allocated reduction var1323 // ptr1324 builder.CreateStore(phis[0], byRefVars[i]);1325 1326 privateReductionVariables[i] = byRefVars[i];1327 moduleTranslation.mapValue(reductionArgs[i], phis[0]);1328 reductionVariableMap.try_emplace(op.getReductionVars()[i], phis[0]);1329 } else {1330 // for by-ref case the store is inside of the reduction region1331 builder.CreateStore(phis[0], privateReductionVariables[i]);1332 // the rest was handled in allocByValReductionVars1333 }1334 1335 // forget the mapping for the initializer region because we might need a1336 // different mapping if this reduction declaration is re-used for a1337 // different variable1338 moduleTranslation.forgetMapping(reductionDecls[i].getInitializerRegion());1339 }1340 1341 return success();1342}1343 1344/// Collect reduction info1345template <typename T>1346static void collectReductionInfo(1347 T loop, llvm::IRBuilderBase &builder,1348 LLVM::ModuleTranslation &moduleTranslation,1349 SmallVectorImpl<omp::DeclareReductionOp> &reductionDecls,1350 SmallVectorImpl<OwningReductionGen> &owningReductionGens,1351 SmallVectorImpl<OwningAtomicReductionGen> &owningAtomicReductionGens,1352 SmallVector<OwningDataPtrPtrReductionGen> &owningDataPtrPtrReductionGens,1353 const ArrayRef<llvm::Value *> privateReductionVariables,1354 SmallVectorImpl<llvm::OpenMPIRBuilder::ReductionInfo> &reductionInfos,1355 ArrayRef<bool> isByRef) {1356 unsigned numReductions = loop.getNumReductionVars();1357 1358 for (unsigned i = 0; i < numReductions; ++i) {1359 owningReductionGens.push_back(1360 makeReductionGen(reductionDecls[i], builder, moduleTranslation));1361 owningAtomicReductionGens.push_back(1362 makeAtomicReductionGen(reductionDecls[i], builder, moduleTranslation));1363 owningDataPtrPtrReductionGens.push_back(makeRefDataPtrGen(1364 reductionDecls[i], builder, moduleTranslation, isByRef[i]));1365 }1366 1367 // Collect the reduction information.1368 reductionInfos.reserve(numReductions);1369 for (unsigned i = 0; i < numReductions; ++i) {1370 llvm::OpenMPIRBuilder::ReductionGenAtomicCBTy atomicGen = nullptr;1371 if (owningAtomicReductionGens[i])1372 atomicGen = owningAtomicReductionGens[i];1373 llvm::Value *variable =1374 moduleTranslation.lookupValue(loop.getReductionVars()[i]);1375 mlir::Type allocatedType;1376 reductionDecls[i].getAllocRegion().walk([&](mlir::Operation *op) {1377 if (auto alloca = mlir::dyn_cast<LLVM::AllocaOp>(op)) {1378 allocatedType = alloca.getElemType();1379 return mlir::WalkResult::interrupt();1380 }1381 1382 return mlir::WalkResult::advance();1383 });1384 1385 reductionInfos.push_back(1386 {moduleTranslation.convertType(reductionDecls[i].getType()), variable,1387 privateReductionVariables[i],1388 /*EvaluationKind=*/llvm::OpenMPIRBuilder::EvalKind::Scalar,1389 owningReductionGens[i],1390 /*ReductionGenClang=*/nullptr, atomicGen,1391 owningDataPtrPtrReductionGens[i],1392 allocatedType ? moduleTranslation.convertType(allocatedType) : nullptr,1393 reductionDecls[i].getByrefElementType()1394 ? moduleTranslation.convertType(1395 *reductionDecls[i].getByrefElementType())1396 : nullptr});1397 }1398}1399 1400/// handling of DeclareReductionOp's cleanup region1401static LogicalResult1402inlineOmpRegionCleanup(llvm::SmallVectorImpl<Region *> &cleanupRegions,1403 llvm::ArrayRef<llvm::Value *> privateVariables,1404 LLVM::ModuleTranslation &moduleTranslation,1405 llvm::IRBuilderBase &builder, StringRef regionName,1406 bool shouldLoadCleanupRegionArg = true) {1407 for (auto [i, cleanupRegion] : llvm::enumerate(cleanupRegions)) {1408 if (cleanupRegion->empty())1409 continue;1410 1411 // map the argument to the cleanup region1412 Block &entry = cleanupRegion->front();1413 1414 llvm::Instruction *potentialTerminator =1415 builder.GetInsertBlock()->empty() ? nullptr1416 : &builder.GetInsertBlock()->back();1417 if (potentialTerminator && potentialTerminator->isTerminator())1418 builder.SetInsertPoint(potentialTerminator);1419 llvm::Value *privateVarValue =1420 shouldLoadCleanupRegionArg1421 ? builder.CreateLoad(1422 moduleTranslation.convertType(entry.getArgument(0).getType()),1423 privateVariables[i])1424 : privateVariables[i];1425 1426 moduleTranslation.mapValue(entry.getArgument(0), privateVarValue);1427 1428 if (failed(inlineConvertOmpRegions(*cleanupRegion, regionName, builder,1429 moduleTranslation)))1430 return failure();1431 1432 // clear block argument mapping in case it needs to be re-created with a1433 // different source for another use of the same reduction decl1434 moduleTranslation.forgetMapping(*cleanupRegion);1435 }1436 return success();1437}1438 1439// TODO: not used by ParallelOp1440template <class OP>1441static LogicalResult createReductionsAndCleanup(1442 OP op, llvm::IRBuilderBase &builder,1443 LLVM::ModuleTranslation &moduleTranslation,1444 llvm::OpenMPIRBuilder::InsertPointTy &allocaIP,1445 SmallVectorImpl<omp::DeclareReductionOp> &reductionDecls,1446 ArrayRef<llvm::Value *> privateReductionVariables, ArrayRef<bool> isByRef,1447 bool isNowait = false, bool isTeamsReduction = false) {1448 // Process the reductions if required.1449 if (op.getNumReductionVars() == 0)1450 return success();1451 1452 SmallVector<OwningReductionGen> owningReductionGens;1453 SmallVector<OwningAtomicReductionGen> owningAtomicReductionGens;1454 SmallVector<OwningDataPtrPtrReductionGen> owningReductionGenRefDataPtrGens;1455 SmallVector<llvm::OpenMPIRBuilder::ReductionInfo, 2> reductionInfos;1456 1457 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();1458 1459 // Create the reduction generators. We need to own them here because1460 // ReductionInfo only accepts references to the generators.1461 collectReductionInfo(op, builder, moduleTranslation, reductionDecls,1462 owningReductionGens, owningAtomicReductionGens,1463 owningReductionGenRefDataPtrGens,1464 privateReductionVariables, reductionInfos, isByRef);1465 1466 // The call to createReductions below expects the block to have a1467 // terminator. Create an unreachable instruction to serve as terminator1468 // and remove it later.1469 llvm::UnreachableInst *tempTerminator = builder.CreateUnreachable();1470 builder.SetInsertPoint(tempTerminator);1471 llvm::OpenMPIRBuilder::InsertPointOrErrorTy contInsertPoint =1472 ompBuilder->createReductions(builder.saveIP(), allocaIP, reductionInfos,1473 isByRef, isNowait, isTeamsReduction);1474 1475 if (failed(handleError(contInsertPoint, *op)))1476 return failure();1477 1478 if (!contInsertPoint->getBlock())1479 return op->emitOpError() << "failed to convert reductions";1480 1481 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =1482 ompBuilder->createBarrier(*contInsertPoint, llvm::omp::OMPD_for);1483 1484 if (failed(handleError(afterIP, *op)))1485 return failure();1486 1487 tempTerminator->eraseFromParent();1488 builder.restoreIP(*afterIP);1489 1490 // after the construct, deallocate private reduction variables1491 SmallVector<Region *> reductionRegions;1492 llvm::transform(reductionDecls, std::back_inserter(reductionRegions),1493 [](omp::DeclareReductionOp reductionDecl) {1494 return &reductionDecl.getCleanupRegion();1495 });1496 return inlineOmpRegionCleanup(reductionRegions, privateReductionVariables,1497 moduleTranslation, builder,1498 "omp.reduction.cleanup");1499 return success();1500}1501 1502static ArrayRef<bool> getIsByRef(std::optional<ArrayRef<bool>> attr) {1503 if (!attr)1504 return {};1505 return *attr;1506}1507 1508// TODO: not used by omp.parallel1509template <typename OP>1510static LogicalResult allocAndInitializeReductionVars(1511 OP op, ArrayRef<BlockArgument> reductionArgs, llvm::IRBuilderBase &builder,1512 LLVM::ModuleTranslation &moduleTranslation,1513 llvm::OpenMPIRBuilder::InsertPointTy &allocaIP,1514 SmallVectorImpl<omp::DeclareReductionOp> &reductionDecls,1515 SmallVectorImpl<llvm::Value *> &privateReductionVariables,1516 DenseMap<Value, llvm::Value *> &reductionVariableMap,1517 llvm::ArrayRef<bool> isByRef) {1518 if (op.getNumReductionVars() == 0)1519 return success();1520 1521 SmallVector<DeferredStore> deferredStores;1522 1523 if (failed(allocReductionVars(op, reductionArgs, builder, moduleTranslation,1524 allocaIP, reductionDecls,1525 privateReductionVariables, reductionVariableMap,1526 deferredStores, isByRef)))1527 return failure();1528 1529 return initReductionVars(op, reductionArgs, builder, moduleTranslation,1530 allocaIP.getBlock(), reductionDecls,1531 privateReductionVariables, reductionVariableMap,1532 isByRef, deferredStores);1533}1534 1535/// Return the llvm::Value * corresponding to the `privateVar` that1536/// is being privatized. It isn't always as simple as looking up1537/// moduleTranslation with privateVar. For instance, in case of1538/// an allocatable, the descriptor for the allocatable is privatized.1539/// This descriptor is mapped using an MapInfoOp. So, this function1540/// will return a pointer to the llvm::Value corresponding to the1541/// block argument for the mapped descriptor.1542static llvm::Value *1543findAssociatedValue(Value privateVar, llvm::IRBuilderBase &builder,1544 LLVM::ModuleTranslation &moduleTranslation,1545 llvm::DenseMap<Value, Value> *mappedPrivateVars = nullptr) {1546 if (mappedPrivateVars == nullptr || !mappedPrivateVars->contains(privateVar))1547 return moduleTranslation.lookupValue(privateVar);1548 1549 Value blockArg = (*mappedPrivateVars)[privateVar];1550 Type privVarType = privateVar.getType();1551 Type blockArgType = blockArg.getType();1552 assert(isa<LLVM::LLVMPointerType>(blockArgType) &&1553 "A block argument corresponding to a mapped var should have "1554 "!llvm.ptr type");1555 1556 if (privVarType == blockArgType)1557 return moduleTranslation.lookupValue(blockArg);1558 1559 // This typically happens when the privatized type is lowered from1560 // boxchar<KIND> and gets lowered to !llvm.struct<(ptr, i64)>. That is the1561 // struct/pair is passed by value. But, mapped values are passed only as1562 // pointers, so before we privatize, we must load the pointer.1563 if (!isa<LLVM::LLVMPointerType>(privVarType))1564 return builder.CreateLoad(moduleTranslation.convertType(privVarType),1565 moduleTranslation.lookupValue(blockArg));1566 1567 return moduleTranslation.lookupValue(privateVar);1568}1569 1570/// Initialize a single (first)private variable. You probably want to use1571/// allocateAndInitPrivateVars instead of this.1572/// This returns the private variable which has been initialized. This1573/// variable should be mapped before constructing the body of the Op.1574static llvm::Expected<llvm::Value *> initPrivateVar(1575 llvm::IRBuilderBase &builder, LLVM::ModuleTranslation &moduleTranslation,1576 omp::PrivateClauseOp &privDecl, Value mlirPrivVar, BlockArgument &blockArg,1577 llvm::Value *llvmPrivateVar, llvm::BasicBlock *privInitBlock,1578 llvm::DenseMap<Value, Value> *mappedPrivateVars = nullptr) {1579 Region &initRegion = privDecl.getInitRegion();1580 if (initRegion.empty())1581 return llvmPrivateVar;1582 1583 // map initialization region block arguments1584 llvm::Value *nonPrivateVar = findAssociatedValue(1585 mlirPrivVar, builder, moduleTranslation, mappedPrivateVars);1586 assert(nonPrivateVar);1587 moduleTranslation.mapValue(privDecl.getInitMoldArg(), nonPrivateVar);1588 moduleTranslation.mapValue(privDecl.getInitPrivateArg(), llvmPrivateVar);1589 1590 // in-place convert the private initialization region1591 SmallVector<llvm::Value *, 1> phis;1592 if (failed(inlineConvertOmpRegions(initRegion, "omp.private.init", builder,1593 moduleTranslation, &phis)))1594 return llvm::createStringError(1595 "failed to inline `init` region of `omp.private`");1596 1597 assert(phis.size() == 1 && "expected one allocation to be yielded");1598 1599 // clear init region block argument mapping in case it needs to be1600 // re-created with a different source for another use of the same1601 // reduction decl1602 moduleTranslation.forgetMapping(initRegion);1603 1604 // Prefer the value yielded from the init region to the allocated private1605 // variable in case the region is operating on arguments by-value (e.g.1606 // Fortran character boxes).1607 return phis[0];1608}1609 1610static llvm::Error1611initPrivateVars(llvm::IRBuilderBase &builder,1612 LLVM::ModuleTranslation &moduleTranslation,1613 PrivateVarsInfo &privateVarsInfo,1614 llvm::DenseMap<Value, Value> *mappedPrivateVars = nullptr) {1615 if (privateVarsInfo.blockArgs.empty())1616 return llvm::Error::success();1617 1618 llvm::BasicBlock *privInitBlock = splitBB(builder, true, "omp.private.init");1619 setInsertPointForPossiblyEmptyBlock(builder, privInitBlock);1620 1621 for (auto [idx, zip] : llvm::enumerate(llvm::zip_equal(1622 privateVarsInfo.privatizers, privateVarsInfo.mlirVars,1623 privateVarsInfo.blockArgs, privateVarsInfo.llvmVars))) {1624 auto [privDecl, mlirPrivVar, blockArg, llvmPrivateVar] = zip;1625 llvm::Expected<llvm::Value *> privVarOrErr = initPrivateVar(1626 builder, moduleTranslation, privDecl, mlirPrivVar, blockArg,1627 llvmPrivateVar, privInitBlock, mappedPrivateVars);1628 1629 if (!privVarOrErr)1630 return privVarOrErr.takeError();1631 1632 llvmPrivateVar = privVarOrErr.get();1633 moduleTranslation.mapValue(blockArg, llvmPrivateVar);1634 1635 setInsertPointForPossiblyEmptyBlock(builder);1636 }1637 1638 return llvm::Error::success();1639}1640 1641/// Allocate and initialize delayed private variables. Returns the basic block1642/// which comes after all of these allocations. llvm::Value * for each of these1643/// private variables are populated in llvmPrivateVars.1644static llvm::Expected<llvm::BasicBlock *>1645allocatePrivateVars(llvm::IRBuilderBase &builder,1646 LLVM::ModuleTranslation &moduleTranslation,1647 PrivateVarsInfo &privateVarsInfo,1648 const llvm::OpenMPIRBuilder::InsertPointTy &allocaIP,1649 llvm::DenseMap<Value, Value> *mappedPrivateVars = nullptr) {1650 // Allocate private vars1651 llvm::Instruction *allocaTerminator = allocaIP.getBlock()->getTerminator();1652 splitBB(llvm::OpenMPIRBuilder::InsertPointTy(allocaIP.getBlock(),1653 allocaTerminator->getIterator()),1654 true, allocaTerminator->getStableDebugLoc(),1655 "omp.region.after_alloca");1656 1657 llvm::IRBuilderBase::InsertPointGuard guard(builder);1658 // Update the allocaTerminator since the alloca block was split above.1659 allocaTerminator = allocaIP.getBlock()->getTerminator();1660 builder.SetInsertPoint(allocaTerminator);1661 // The new terminator is an uncondition branch created by the splitBB above.1662 assert(allocaTerminator->getNumSuccessors() == 1 &&1663 "This is an unconditional branch created by splitBB");1664 1665 llvm::DataLayout dataLayout = builder.GetInsertBlock()->getDataLayout();1666 llvm::BasicBlock *afterAllocas = allocaTerminator->getSuccessor(0);1667 1668 unsigned int allocaAS =1669 moduleTranslation.getLLVMModule()->getDataLayout().getAllocaAddrSpace();1670 unsigned int defaultAS = moduleTranslation.getLLVMModule()1671 ->getDataLayout()1672 .getProgramAddressSpace();1673 1674 for (auto [privDecl, mlirPrivVar, blockArg] :1675 llvm::zip_equal(privateVarsInfo.privatizers, privateVarsInfo.mlirVars,1676 privateVarsInfo.blockArgs)) {1677 llvm::Type *llvmAllocType =1678 moduleTranslation.convertType(privDecl.getType());1679 builder.SetInsertPoint(allocaIP.getBlock()->getTerminator());1680 llvm::Value *llvmPrivateVar = builder.CreateAlloca(1681 llvmAllocType, /*ArraySize=*/nullptr, "omp.private.alloc");1682 if (allocaAS != defaultAS)1683 llvmPrivateVar = builder.CreateAddrSpaceCast(llvmPrivateVar,1684 builder.getPtrTy(defaultAS));1685 1686 privateVarsInfo.llvmVars.push_back(llvmPrivateVar);1687 }1688 1689 return afterAllocas;1690}1691 1692static LogicalResult copyFirstPrivateVars(1693 mlir::Operation *op, llvm::IRBuilderBase &builder,1694 LLVM::ModuleTranslation &moduleTranslation,1695 SmallVectorImpl<mlir::Value> &mlirPrivateVars,1696 ArrayRef<llvm::Value *> llvmPrivateVars,1697 SmallVectorImpl<omp::PrivateClauseOp> &privateDecls, bool insertBarrier,1698 llvm::DenseMap<Value, Value> *mappedPrivateVars = nullptr) {1699 // Apply copy region for firstprivate.1700 bool needsFirstprivate =1701 llvm::any_of(privateDecls, [](omp::PrivateClauseOp &privOp) {1702 return privOp.getDataSharingType() ==1703 omp::DataSharingClauseType::FirstPrivate;1704 });1705 1706 if (!needsFirstprivate)1707 return success();1708 1709 llvm::BasicBlock *copyBlock =1710 splitBB(builder, /*CreateBranch=*/true, "omp.private.copy");1711 setInsertPointForPossiblyEmptyBlock(builder, copyBlock);1712 1713 for (auto [decl, mlirVar, llvmVar] :1714 llvm::zip_equal(privateDecls, mlirPrivateVars, llvmPrivateVars)) {1715 if (decl.getDataSharingType() != omp::DataSharingClauseType::FirstPrivate)1716 continue;1717 1718 // copyRegion implements `lhs = rhs`1719 Region ©Region = decl.getCopyRegion();1720 1721 // map copyRegion rhs arg1722 llvm::Value *nonPrivateVar = findAssociatedValue(1723 mlirVar, builder, moduleTranslation, mappedPrivateVars);1724 assert(nonPrivateVar);1725 moduleTranslation.mapValue(decl.getCopyMoldArg(), nonPrivateVar);1726 1727 // map copyRegion lhs arg1728 moduleTranslation.mapValue(decl.getCopyPrivateArg(), llvmVar);1729 1730 // in-place convert copy region1731 if (failed(inlineConvertOmpRegions(copyRegion, "omp.private.copy", builder,1732 moduleTranslation)))1733 return decl.emitError("failed to inline `copy` region of `omp.private`");1734 1735 setInsertPointForPossiblyEmptyBlock(builder);1736 1737 // ignore unused value yielded from copy region1738 1739 // clear copy region block argument mapping in case it needs to be1740 // re-created with different sources for reuse of the same reduction1741 // decl1742 moduleTranslation.forgetMapping(copyRegion);1743 }1744 1745 if (insertBarrier) {1746 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();1747 llvm::OpenMPIRBuilder::InsertPointOrErrorTy res =1748 ompBuilder->createBarrier(builder.saveIP(), llvm::omp::OMPD_barrier);1749 if (failed(handleError(res, *op)))1750 return failure();1751 }1752 1753 return success();1754}1755 1756static LogicalResult1757cleanupPrivateVars(llvm::IRBuilderBase &builder,1758 LLVM::ModuleTranslation &moduleTranslation, Location loc,1759 SmallVectorImpl<llvm::Value *> &llvmPrivateVars,1760 SmallVectorImpl<omp::PrivateClauseOp> &privateDecls) {1761 // private variable deallocation1762 SmallVector<Region *> privateCleanupRegions;1763 llvm::transform(privateDecls, std::back_inserter(privateCleanupRegions),1764 [](omp::PrivateClauseOp privatizer) {1765 return &privatizer.getDeallocRegion();1766 });1767 1768 if (failed(inlineOmpRegionCleanup(1769 privateCleanupRegions, llvmPrivateVars, moduleTranslation, builder,1770 "omp.private.dealloc", /*shouldLoadCleanupRegionArg=*/false)))1771 return mlir::emitError(loc, "failed to inline `dealloc` region of an "1772 "`omp.private` op in");1773 1774 return success();1775}1776 1777/// Returns true if the construct contains omp.cancel or omp.cancellation_point1778static bool constructIsCancellable(Operation *op) {1779 // omp.cancel and omp.cancellation_point must be "closely nested" so they will1780 // be visible and not inside of function calls. This is enforced by the1781 // verifier.1782 return op1783 ->walk([](Operation *child) {1784 if (mlir::isa<omp::CancelOp, omp::CancellationPointOp>(child))1785 return WalkResult::interrupt();1786 return WalkResult::advance();1787 })1788 .wasInterrupted();1789}1790 1791static LogicalResult1792convertOmpSections(Operation &opInst, llvm::IRBuilderBase &builder,1793 LLVM::ModuleTranslation &moduleTranslation) {1794 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;1795 using StorableBodyGenCallbackTy =1796 llvm::OpenMPIRBuilder::StorableBodyGenCallbackTy;1797 1798 auto sectionsOp = cast<omp::SectionsOp>(opInst);1799 1800 if (failed(checkImplementationStatus(opInst)))1801 return failure();1802 1803 llvm::ArrayRef<bool> isByRef = getIsByRef(sectionsOp.getReductionByref());1804 assert(isByRef.size() == sectionsOp.getNumReductionVars());1805 1806 SmallVector<omp::DeclareReductionOp> reductionDecls;1807 collectReductionDecls(sectionsOp, reductionDecls);1808 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =1809 findAllocaInsertPoint(builder, moduleTranslation);1810 1811 SmallVector<llvm::Value *> privateReductionVariables(1812 sectionsOp.getNumReductionVars());1813 DenseMap<Value, llvm::Value *> reductionVariableMap;1814 1815 MutableArrayRef<BlockArgument> reductionArgs =1816 cast<omp::BlockArgOpenMPOpInterface>(opInst).getReductionBlockArgs();1817 1818 if (failed(allocAndInitializeReductionVars(1819 sectionsOp, reductionArgs, builder, moduleTranslation, allocaIP,1820 reductionDecls, privateReductionVariables, reductionVariableMap,1821 isByRef)))1822 return failure();1823 1824 SmallVector<StorableBodyGenCallbackTy> sectionCBs;1825 1826 for (Operation &op : *sectionsOp.getRegion().begin()) {1827 auto sectionOp = dyn_cast<omp::SectionOp>(op);1828 if (!sectionOp) // omp.terminator1829 continue;1830 1831 Region ®ion = sectionOp.getRegion();1832 auto sectionCB = [§ionsOp, ®ion, &builder, &moduleTranslation](1833 InsertPointTy allocaIP, InsertPointTy codeGenIP) {1834 builder.restoreIP(codeGenIP);1835 1836 // map the omp.section reduction block argument to the omp.sections block1837 // arguments1838 // TODO: this assumes that the only block arguments are reduction1839 // variables1840 assert(region.getNumArguments() ==1841 sectionsOp.getRegion().getNumArguments());1842 for (auto [sectionsArg, sectionArg] : llvm::zip_equal(1843 sectionsOp.getRegion().getArguments(), region.getArguments())) {1844 llvm::Value *llvmVal = moduleTranslation.lookupValue(sectionsArg);1845 assert(llvmVal);1846 moduleTranslation.mapValue(sectionArg, llvmVal);1847 }1848 1849 return convertOmpOpRegions(region, "omp.section.region", builder,1850 moduleTranslation)1851 .takeError();1852 };1853 sectionCBs.push_back(sectionCB);1854 }1855 1856 // No sections within omp.sections operation - skip generation. This situation1857 // is only possible if there is only a terminator operation inside the1858 // sections operation1859 if (sectionCBs.empty())1860 return success();1861 1862 assert(isa<omp::SectionOp>(*sectionsOp.getRegion().op_begin()));1863 1864 // TODO: Perform appropriate actions according to the data-sharing1865 // attribute (shared, private, firstprivate, ...) of variables.1866 // Currently defaults to shared.1867 auto privCB = [&](InsertPointTy, InsertPointTy codeGenIP, llvm::Value &,1868 llvm::Value &vPtr, llvm::Value *&replacementValue)1869 -> llvm::OpenMPIRBuilder::InsertPointOrErrorTy {1870 replacementValue = &vPtr;1871 return codeGenIP;1872 };1873 1874 // TODO: Perform finalization actions for variables. This has to be1875 // called for variables which have destructors/finalizers.1876 auto finiCB = [&](InsertPointTy codeGenIP) { return llvm::Error::success(); };1877 1878 allocaIP = findAllocaInsertPoint(builder, moduleTranslation);1879 bool isCancellable = constructIsCancellable(sectionsOp);1880 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);1881 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =1882 moduleTranslation.getOpenMPBuilder()->createSections(1883 ompLoc, allocaIP, sectionCBs, privCB, finiCB, isCancellable,1884 sectionsOp.getNowait());1885 1886 if (failed(handleError(afterIP, opInst)))1887 return failure();1888 1889 builder.restoreIP(*afterIP);1890 1891 // Process the reductions if required.1892 return createReductionsAndCleanup(1893 sectionsOp, builder, moduleTranslation, allocaIP, reductionDecls,1894 privateReductionVariables, isByRef, sectionsOp.getNowait());1895}1896 1897/// Converts an OpenMP single construct into LLVM IR using OpenMPIRBuilder.1898static LogicalResult1899convertOmpSingle(omp::SingleOp &singleOp, llvm::IRBuilderBase &builder,1900 LLVM::ModuleTranslation &moduleTranslation) {1901 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;1902 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);1903 1904 if (failed(checkImplementationStatus(*singleOp)))1905 return failure();1906 1907 auto bodyCB = [&](InsertPointTy allocaIP, InsertPointTy codegenIP) {1908 builder.restoreIP(codegenIP);1909 return convertOmpOpRegions(singleOp.getRegion(), "omp.single.region",1910 builder, moduleTranslation)1911 .takeError();1912 };1913 auto finiCB = [&](InsertPointTy codeGenIP) { return llvm::Error::success(); };1914 1915 // Handle copyprivate1916 Operation::operand_range cpVars = singleOp.getCopyprivateVars();1917 std::optional<ArrayAttr> cpFuncs = singleOp.getCopyprivateSyms();1918 llvm::SmallVector<llvm::Value *> llvmCPVars;1919 llvm::SmallVector<llvm::Function *> llvmCPFuncs;1920 for (size_t i = 0, e = cpVars.size(); i < e; ++i) {1921 llvmCPVars.push_back(moduleTranslation.lookupValue(cpVars[i]));1922 auto llvmFuncOp = SymbolTable::lookupNearestSymbolFrom<LLVM::LLVMFuncOp>(1923 singleOp, cast<SymbolRefAttr>((*cpFuncs)[i]));1924 llvmCPFuncs.push_back(1925 moduleTranslation.lookupFunction(llvmFuncOp.getName()));1926 }1927 1928 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =1929 moduleTranslation.getOpenMPBuilder()->createSingle(1930 ompLoc, bodyCB, finiCB, singleOp.getNowait(), llvmCPVars,1931 llvmCPFuncs);1932 1933 if (failed(handleError(afterIP, *singleOp)))1934 return failure();1935 1936 builder.restoreIP(*afterIP);1937 return success();1938}1939 1940static bool teamsReductionContainedInDistribute(omp::TeamsOp teamsOp) {1941 auto iface =1942 llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(teamsOp.getOperation());1943 // Check that all uses of the reduction block arg has the same distribute op1944 // parent.1945 llvm::SmallVector<mlir::Operation *> debugUses;1946 Operation *distOp = nullptr;1947 for (auto ra : iface.getReductionBlockArgs())1948 for (auto &use : ra.getUses()) {1949 auto *useOp = use.getOwner();1950 // Ignore debug uses.1951 if (mlir::isa<LLVM::DbgDeclareOp, LLVM::DbgValueOp>(useOp)) {1952 debugUses.push_back(useOp);1953 continue;1954 }1955 1956 auto currentDistOp = useOp->getParentOfType<omp::DistributeOp>();1957 // Use is not inside a distribute op - return false1958 if (!currentDistOp)1959 return false;1960 // Multiple distribute operations - return false1961 Operation *currentOp = currentDistOp.getOperation();1962 if (distOp && (distOp != currentOp))1963 return false;1964 1965 distOp = currentOp;1966 }1967 1968 // If we are going to use distribute reduction then remove any debug uses of1969 // the reduction parameters in teamsOp. Otherwise they will be left without1970 // any mapped value in moduleTranslation and will eventually error out.1971 for (auto use : debugUses)1972 use->erase();1973 return true;1974}1975 1976// Convert an OpenMP Teams construct to LLVM IR using OpenMPIRBuilder1977static LogicalResult1978convertOmpTeams(omp::TeamsOp op, llvm::IRBuilderBase &builder,1979 LLVM::ModuleTranslation &moduleTranslation) {1980 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;1981 if (failed(checkImplementationStatus(*op)))1982 return failure();1983 1984 DenseMap<Value, llvm::Value *> reductionVariableMap;1985 unsigned numReductionVars = op.getNumReductionVars();1986 SmallVector<omp::DeclareReductionOp> reductionDecls;1987 SmallVector<llvm::Value *> privateReductionVariables(numReductionVars);1988 llvm::ArrayRef<bool> isByRef;1989 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =1990 findAllocaInsertPoint(builder, moduleTranslation);1991 1992 // Only do teams reduction if there is no distribute op that captures the1993 // reduction instead.1994 bool doTeamsReduction = !teamsReductionContainedInDistribute(op);1995 if (doTeamsReduction) {1996 isByRef = getIsByRef(op.getReductionByref());1997 1998 assert(isByRef.size() == op.getNumReductionVars());1999 2000 MutableArrayRef<BlockArgument> reductionArgs =2001 llvm::cast<omp::BlockArgOpenMPOpInterface>(*op).getReductionBlockArgs();2002 2003 collectReductionDecls(op, reductionDecls);2004 2005 if (failed(allocAndInitializeReductionVars(2006 op, reductionArgs, builder, moduleTranslation, allocaIP,2007 reductionDecls, privateReductionVariables, reductionVariableMap,2008 isByRef)))2009 return failure();2010 }2011 2012 auto bodyCB = [&](InsertPointTy allocaIP, InsertPointTy codegenIP) {2013 LLVM::ModuleTranslation::SaveStack<OpenMPAllocaStackFrame> frame(2014 moduleTranslation, allocaIP);2015 builder.restoreIP(codegenIP);2016 return convertOmpOpRegions(op.getRegion(), "omp.teams.region", builder,2017 moduleTranslation)2018 .takeError();2019 };2020 2021 llvm::Value *numTeamsLower = nullptr;2022 if (Value numTeamsLowerVar = op.getNumTeamsLower())2023 numTeamsLower = moduleTranslation.lookupValue(numTeamsLowerVar);2024 2025 llvm::Value *numTeamsUpper = nullptr;2026 if (Value numTeamsUpperVar = op.getNumTeamsUpper())2027 numTeamsUpper = moduleTranslation.lookupValue(numTeamsUpperVar);2028 2029 llvm::Value *threadLimit = nullptr;2030 if (Value threadLimitVar = op.getThreadLimit())2031 threadLimit = moduleTranslation.lookupValue(threadLimitVar);2032 2033 llvm::Value *ifExpr = nullptr;2034 if (Value ifVar = op.getIfExpr())2035 ifExpr = moduleTranslation.lookupValue(ifVar);2036 2037 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);2038 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =2039 moduleTranslation.getOpenMPBuilder()->createTeams(2040 ompLoc, bodyCB, numTeamsLower, numTeamsUpper, threadLimit, ifExpr);2041 2042 if (failed(handleError(afterIP, *op)))2043 return failure();2044 2045 builder.restoreIP(*afterIP);2046 if (doTeamsReduction) {2047 // Process the reductions if required.2048 return createReductionsAndCleanup(2049 op, builder, moduleTranslation, allocaIP, reductionDecls,2050 privateReductionVariables, isByRef,2051 /*isNoWait*/ false, /*isTeamsReduction*/ true);2052 }2053 return success();2054}2055 2056static void2057buildDependData(std::optional<ArrayAttr> dependKinds, OperandRange dependVars,2058 LLVM::ModuleTranslation &moduleTranslation,2059 SmallVectorImpl<llvm::OpenMPIRBuilder::DependData> &dds) {2060 if (dependVars.empty())2061 return;2062 for (auto dep : llvm::zip(dependVars, dependKinds->getValue())) {2063 llvm::omp::RTLDependenceKindTy type;2064 switch (2065 cast<mlir::omp::ClauseTaskDependAttr>(std::get<1>(dep)).getValue()) {2066 case mlir::omp::ClauseTaskDepend::taskdependin:2067 type = llvm::omp::RTLDependenceKindTy::DepIn;2068 break;2069 // The OpenMP runtime requires that the codegen for 'depend' clause for2070 // 'out' dependency kind must be the same as codegen for 'depend' clause2071 // with 'inout' dependency.2072 case mlir::omp::ClauseTaskDepend::taskdependout:2073 case mlir::omp::ClauseTaskDepend::taskdependinout:2074 type = llvm::omp::RTLDependenceKindTy::DepInOut;2075 break;2076 case mlir::omp::ClauseTaskDepend::taskdependmutexinoutset:2077 type = llvm::omp::RTLDependenceKindTy::DepMutexInOutSet;2078 break;2079 case mlir::omp::ClauseTaskDepend::taskdependinoutset:2080 type = llvm::omp::RTLDependenceKindTy::DepInOutSet;2081 break;2082 };2083 llvm::Value *depVal = moduleTranslation.lookupValue(std::get<0>(dep));2084 llvm::OpenMPIRBuilder::DependData dd(type, depVal->getType(), depVal);2085 dds.emplace_back(dd);2086 }2087}2088 2089/// Shared implementation of a callback which adds a termiator for the new block2090/// created for the branch taken when an openmp construct is cancelled. The2091/// terminator is saved in \p cancelTerminators. This callback is invoked only2092/// if there is cancellation inside of the taskgroup body.2093/// The terminator will need to be fixed to branch to the correct block to2094/// cleanup the construct.2095static void2096pushCancelFinalizationCB(SmallVectorImpl<llvm::BranchInst *> &cancelTerminators,2097 llvm::IRBuilderBase &llvmBuilder,2098 llvm::OpenMPIRBuilder &ompBuilder, mlir::Operation *op,2099 llvm::omp::Directive cancelDirective) {2100 auto finiCB = [&](llvm::OpenMPIRBuilder::InsertPointTy ip) -> llvm::Error {2101 llvm::IRBuilderBase::InsertPointGuard guard(llvmBuilder);2102 2103 // ip is currently in the block branched to if cancellation occured.2104 // We need to create a branch to terminate that block.2105 llvmBuilder.restoreIP(ip);2106 2107 // We must still clean up the construct after cancelling it, so we need to2108 // branch to the block that finalizes the taskgroup.2109 // That block has not been created yet so use this block as a dummy for now2110 // and fix this after creating the operation.2111 cancelTerminators.push_back(llvmBuilder.CreateBr(ip.getBlock()));2112 return llvm::Error::success();2113 };2114 // We have to add the cleanup to the OpenMPIRBuilder before the body gets2115 // created in case the body contains omp.cancel (which will then expect to be2116 // able to find this cleanup callback).2117 ompBuilder.pushFinalizationCB(2118 {finiCB, cancelDirective, constructIsCancellable(op)});2119}2120 2121/// If we cancelled the construct, we should branch to the finalization block of2122/// that construct. OMPIRBuilder structures the CFG such that the cleanup block2123/// is immediately before the continuation block. Now this finalization has2124/// been created we can fix the branch.2125static void2126popCancelFinalizationCB(const ArrayRef<llvm::BranchInst *> cancelTerminators,2127 llvm::OpenMPIRBuilder &ompBuilder,2128 const llvm::OpenMPIRBuilder::InsertPointTy &afterIP) {2129 ompBuilder.popFinalizationCB();2130 llvm::BasicBlock *constructFini = afterIP.getBlock()->getSinglePredecessor();2131 for (llvm::BranchInst *cancelBranch : cancelTerminators) {2132 assert(cancelBranch->getNumSuccessors() == 1 &&2133 "cancel branch should have one target");2134 cancelBranch->setSuccessor(0, constructFini);2135 }2136}2137 2138namespace {2139/// TaskContextStructManager takes care of creating and freeing a structure2140/// containing information needed by the task body to execute.2141class TaskContextStructManager {2142public:2143 TaskContextStructManager(llvm::IRBuilderBase &builder,2144 LLVM::ModuleTranslation &moduleTranslation,2145 MutableArrayRef<omp::PrivateClauseOp> privateDecls)2146 : builder{builder}, moduleTranslation{moduleTranslation},2147 privateDecls{privateDecls} {}2148 2149 /// Creates a heap allocated struct containing space for each private2150 /// variable. Invariant: privateVarTypes, privateDecls, and the elements of2151 /// the structure should all have the same order (although privateDecls which2152 /// do not read from the mold argument are skipped).2153 void generateTaskContextStruct();2154 2155 /// Create GEPs to access each member of the structure representing a private2156 /// variable, adding them to llvmPrivateVars. Null values are added where2157 /// private decls were skipped so that the ordering continues to match the2158 /// private decls.2159 void createGEPsToPrivateVars();2160 2161 /// De-allocate the task context structure.2162 void freeStructPtr();2163 2164 MutableArrayRef<llvm::Value *> getLLVMPrivateVarGEPs() {2165 return llvmPrivateVarGEPs;2166 }2167 2168 llvm::Value *getStructPtr() { return structPtr; }2169 2170private:2171 llvm::IRBuilderBase &builder;2172 LLVM::ModuleTranslation &moduleTranslation;2173 MutableArrayRef<omp::PrivateClauseOp> privateDecls;2174 2175 /// The type of each member of the structure, in order.2176 SmallVector<llvm::Type *> privateVarTypes;2177 2178 /// LLVM values for each private variable, or null if that private variable is2179 /// not included in the task context structure2180 SmallVector<llvm::Value *> llvmPrivateVarGEPs;2181 2182 /// A pointer to the structure containing context for this task.2183 llvm::Value *structPtr = nullptr;2184 /// The type of the structure2185 llvm::Type *structTy = nullptr;2186};2187} // namespace2188 2189void TaskContextStructManager::generateTaskContextStruct() {2190 if (privateDecls.empty())2191 return;2192 privateVarTypes.reserve(privateDecls.size());2193 2194 for (omp::PrivateClauseOp &privOp : privateDecls) {2195 // Skip private variables which can safely be allocated and initialised2196 // inside of the task2197 if (!privOp.readsFromMold())2198 continue;2199 Type mlirType = privOp.getType();2200 privateVarTypes.push_back(moduleTranslation.convertType(mlirType));2201 }2202 2203 structTy = llvm::StructType::get(moduleTranslation.getLLVMContext(),2204 privateVarTypes);2205 2206 llvm::DataLayout dataLayout =2207 builder.GetInsertBlock()->getModule()->getDataLayout();2208 llvm::Type *intPtrTy = builder.getIntPtrTy(dataLayout);2209 llvm::Constant *allocSize = llvm::ConstantExpr::getSizeOf(structTy);2210 2211 // Heap allocate the structure2212 structPtr = builder.CreateMalloc(intPtrTy, structTy, allocSize,2213 /*ArraySize=*/nullptr, /*MallocF=*/nullptr,2214 "omp.task.context_ptr");2215}2216 2217void TaskContextStructManager::createGEPsToPrivateVars() {2218 if (!structPtr) {2219 assert(privateVarTypes.empty());2220 return;2221 }2222 2223 // Create GEPs for each struct member2224 llvmPrivateVarGEPs.clear();2225 llvmPrivateVarGEPs.reserve(privateDecls.size());2226 llvm::Value *zero = builder.getInt32(0);2227 unsigned i = 0;2228 for (auto privDecl : privateDecls) {2229 if (!privDecl.readsFromMold()) {2230 // Handle this inside of the task so we don't pass unnessecary vars in2231 llvmPrivateVarGEPs.push_back(nullptr);2232 continue;2233 }2234 llvm::Value *iVal = builder.getInt32(i);2235 llvm::Value *gep = builder.CreateGEP(structTy, structPtr, {zero, iVal});2236 llvmPrivateVarGEPs.push_back(gep);2237 i += 1;2238 }2239}2240 2241void TaskContextStructManager::freeStructPtr() {2242 if (!structPtr)2243 return;2244 2245 llvm::IRBuilderBase::InsertPointGuard guard{builder};2246 // Ensure we don't put the call to free() after the terminator2247 builder.SetInsertPoint(builder.GetInsertBlock()->getTerminator());2248 builder.CreateFree(structPtr);2249}2250 2251/// Converts an OpenMP task construct into LLVM IR using OpenMPIRBuilder.2252static LogicalResult2253convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,2254 LLVM::ModuleTranslation &moduleTranslation) {2255 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;2256 if (failed(checkImplementationStatus(*taskOp)))2257 return failure();2258 2259 PrivateVarsInfo privateVarsInfo(taskOp);2260 TaskContextStructManager taskStructMgr{builder, moduleTranslation,2261 privateVarsInfo.privatizers};2262 2263 // Allocate and copy private variables before creating the task. This avoids2264 // accessing invalid memory if (after this scope ends) the private variables2265 // are initialized from host variables or if the variables are copied into2266 // from host variables (firstprivate). The insertion point is just before2267 // where the code for creating and scheduling the task will go. That puts this2268 // code outside of the outlined task region, which is what we want because2269 // this way the initialization and copy regions are executed immediately while2270 // the host variable data are still live.2271 2272 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =2273 findAllocaInsertPoint(builder, moduleTranslation);2274 2275 // Not using splitBB() because that requires the current block to have a2276 // terminator.2277 assert(builder.GetInsertPoint() == builder.GetInsertBlock()->end());2278 llvm::BasicBlock *taskStartBlock = llvm::BasicBlock::Create(2279 builder.getContext(), "omp.task.start",2280 /*Parent=*/builder.GetInsertBlock()->getParent());2281 llvm::Instruction *branchToTaskStartBlock = builder.CreateBr(taskStartBlock);2282 builder.SetInsertPoint(branchToTaskStartBlock);2283 2284 // Now do this again to make the initialization and copy blocks2285 llvm::BasicBlock *copyBlock =2286 splitBB(builder, /*CreateBranch=*/true, "omp.private.copy");2287 llvm::BasicBlock *initBlock =2288 splitBB(builder, /*CreateBranch=*/true, "omp.private.init");2289 2290 // Now the control flow graph should look like2291 // starter_block:2292 // <---- where we started when convertOmpTaskOp was called2293 // br %omp.private.init2294 // omp.private.init:2295 // br %omp.private.copy2296 // omp.private.copy:2297 // br %omp.task.start2298 // omp.task.start:2299 // <---- where we want the insertion point to be when we call createTask()2300 2301 // Save the alloca insertion point on ModuleTranslation stack for use in2302 // nested regions.2303 LLVM::ModuleTranslation::SaveStack<OpenMPAllocaStackFrame> frame(2304 moduleTranslation, allocaIP);2305 2306 // Allocate and initialize private variables2307 builder.SetInsertPoint(initBlock->getTerminator());2308 2309 // Create task variable structure2310 taskStructMgr.generateTaskContextStruct();2311 // GEPs so that we can initialize the variables. Don't use these GEPs inside2312 // of the body otherwise it will be the GEP not the struct which is fowarded2313 // to the outlined function. GEPs forwarded in this way are passed in a2314 // stack-allocated (by OpenMPIRBuilder) structure which is not safe for tasks2315 // which may not be executed until after the current stack frame goes out of2316 // scope.2317 taskStructMgr.createGEPsToPrivateVars();2318 2319 for (auto [privDecl, mlirPrivVar, blockArg, llvmPrivateVarAlloc] :2320 llvm::zip_equal(privateVarsInfo.privatizers, privateVarsInfo.mlirVars,2321 privateVarsInfo.blockArgs,2322 taskStructMgr.getLLVMPrivateVarGEPs())) {2323 // To be handled inside the task.2324 if (!privDecl.readsFromMold())2325 continue;2326 assert(llvmPrivateVarAlloc &&2327 "reads from mold so shouldn't have been skipped");2328 2329 llvm::Expected<llvm::Value *> privateVarOrErr =2330 initPrivateVar(builder, moduleTranslation, privDecl, mlirPrivVar,2331 blockArg, llvmPrivateVarAlloc, initBlock);2332 if (!privateVarOrErr)2333 return handleError(privateVarOrErr, *taskOp.getOperation());2334 2335 setInsertPointForPossiblyEmptyBlock(builder);2336 2337 // TODO: this is a bit of a hack for Fortran character boxes.2338 // Character boxes are passed by value into the init region and then the2339 // initialized character box is yielded by value. Here we need to store the2340 // yielded value into the private allocation, and load the private2341 // allocation to match the type expected by region block arguments.2342 if ((privateVarOrErr.get() != llvmPrivateVarAlloc) &&2343 !mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {2344 builder.CreateStore(privateVarOrErr.get(), llvmPrivateVarAlloc);2345 // Load it so we have the value pointed to by the GEP2346 llvmPrivateVarAlloc = builder.CreateLoad(privateVarOrErr.get()->getType(),2347 llvmPrivateVarAlloc);2348 }2349 assert(llvmPrivateVarAlloc->getType() ==2350 moduleTranslation.convertType(blockArg.getType()));2351 2352 // Mapping blockArg -> llvmPrivateVarAlloc is done inside the body callback2353 // so that OpenMPIRBuilder doesn't try to pass each GEP address through a2354 // stack allocated structure.2355 }2356 2357 // firstprivate copy region2358 setInsertPointForPossiblyEmptyBlock(builder, copyBlock);2359 if (failed(copyFirstPrivateVars(2360 taskOp, builder, moduleTranslation, privateVarsInfo.mlirVars,2361 taskStructMgr.getLLVMPrivateVarGEPs(), privateVarsInfo.privatizers,2362 taskOp.getPrivateNeedsBarrier())))2363 return llvm::failure();2364 2365 // Set up for call to createTask()2366 builder.SetInsertPoint(taskStartBlock);2367 2368 auto bodyCB = [&](InsertPointTy allocaIP,2369 InsertPointTy codegenIP) -> llvm::Error {2370 // Save the alloca insertion point on ModuleTranslation stack for use in2371 // nested regions.2372 LLVM::ModuleTranslation::SaveStack<OpenMPAllocaStackFrame> frame(2373 moduleTranslation, allocaIP);2374 2375 // translate the body of the task:2376 builder.restoreIP(codegenIP);2377 2378 llvm::BasicBlock *privInitBlock = nullptr;2379 privateVarsInfo.llvmVars.resize(privateVarsInfo.blockArgs.size());2380 for (auto [i, zip] : llvm::enumerate(llvm::zip_equal(2381 privateVarsInfo.blockArgs, privateVarsInfo.privatizers,2382 privateVarsInfo.mlirVars))) {2383 auto [blockArg, privDecl, mlirPrivVar] = zip;2384 // This is handled before the task executes2385 if (privDecl.readsFromMold())2386 continue;2387 2388 llvm::IRBuilderBase::InsertPointGuard guard(builder);2389 llvm::Type *llvmAllocType =2390 moduleTranslation.convertType(privDecl.getType());2391 builder.SetInsertPoint(allocaIP.getBlock()->getTerminator());2392 llvm::Value *llvmPrivateVar = builder.CreateAlloca(2393 llvmAllocType, /*ArraySize=*/nullptr, "omp.private.alloc");2394 2395 llvm::Expected<llvm::Value *> privateVarOrError =2396 initPrivateVar(builder, moduleTranslation, privDecl, mlirPrivVar,2397 blockArg, llvmPrivateVar, privInitBlock);2398 if (!privateVarOrError)2399 return privateVarOrError.takeError();2400 moduleTranslation.mapValue(blockArg, privateVarOrError.get());2401 privateVarsInfo.llvmVars[i] = privateVarOrError.get();2402 }2403 2404 taskStructMgr.createGEPsToPrivateVars();2405 for (auto [i, llvmPrivVar] :2406 llvm::enumerate(taskStructMgr.getLLVMPrivateVarGEPs())) {2407 if (!llvmPrivVar) {2408 assert(privateVarsInfo.llvmVars[i] &&2409 "This is added in the loop above");2410 continue;2411 }2412 privateVarsInfo.llvmVars[i] = llvmPrivVar;2413 }2414 2415 // Find and map the addresses of each variable within the task context2416 // structure2417 for (auto [blockArg, llvmPrivateVar, privateDecl] :2418 llvm::zip_equal(privateVarsInfo.blockArgs, privateVarsInfo.llvmVars,2419 privateVarsInfo.privatizers)) {2420 // This was handled above.2421 if (!privateDecl.readsFromMold())2422 continue;2423 // Fix broken pass-by-value case for Fortran character boxes2424 if (!mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {2425 llvmPrivateVar = builder.CreateLoad(2426 moduleTranslation.convertType(blockArg.getType()), llvmPrivateVar);2427 }2428 assert(llvmPrivateVar->getType() ==2429 moduleTranslation.convertType(blockArg.getType()));2430 moduleTranslation.mapValue(blockArg, llvmPrivateVar);2431 }2432 2433 auto continuationBlockOrError = convertOmpOpRegions(2434 taskOp.getRegion(), "omp.task.region", builder, moduleTranslation);2435 if (failed(handleError(continuationBlockOrError, *taskOp)))2436 return llvm::make_error<PreviouslyReportedError>();2437 2438 builder.SetInsertPoint(continuationBlockOrError.get()->getTerminator());2439 2440 if (failed(cleanupPrivateVars(builder, moduleTranslation, taskOp.getLoc(),2441 privateVarsInfo.llvmVars,2442 privateVarsInfo.privatizers)))2443 return llvm::make_error<PreviouslyReportedError>();2444 2445 // Free heap allocated task context structure at the end of the task.2446 taskStructMgr.freeStructPtr();2447 2448 return llvm::Error::success();2449 };2450 2451 llvm::OpenMPIRBuilder &ompBuilder = *moduleTranslation.getOpenMPBuilder();2452 SmallVector<llvm::BranchInst *> cancelTerminators;2453 // The directive to match here is OMPD_taskgroup because it is the taskgroup2454 // which is canceled. This is handled here because it is the task's cleanup2455 // block which should be branched to.2456 pushCancelFinalizationCB(cancelTerminators, builder, ompBuilder, taskOp,2457 llvm::omp::Directive::OMPD_taskgroup);2458 2459 SmallVector<llvm::OpenMPIRBuilder::DependData> dds;2460 buildDependData(taskOp.getDependKinds(), taskOp.getDependVars(),2461 moduleTranslation, dds);2462 2463 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);2464 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =2465 moduleTranslation.getOpenMPBuilder()->createTask(2466 ompLoc, allocaIP, bodyCB, !taskOp.getUntied(),2467 moduleTranslation.lookupValue(taskOp.getFinal()),2468 moduleTranslation.lookupValue(taskOp.getIfExpr()), dds,2469 taskOp.getMergeable(),2470 moduleTranslation.lookupValue(taskOp.getEventHandle()),2471 moduleTranslation.lookupValue(taskOp.getPriority()));2472 2473 if (failed(handleError(afterIP, *taskOp)))2474 return failure();2475 2476 // Set the correct branch target for task cancellation2477 popCancelFinalizationCB(cancelTerminators, ompBuilder, afterIP.get());2478 2479 builder.restoreIP(*afterIP);2480 return success();2481}2482 2483/// Converts an OpenMP taskgroup construct into LLVM IR using OpenMPIRBuilder.2484static LogicalResult2485convertOmpTaskgroupOp(omp::TaskgroupOp tgOp, llvm::IRBuilderBase &builder,2486 LLVM::ModuleTranslation &moduleTranslation) {2487 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;2488 if (failed(checkImplementationStatus(*tgOp)))2489 return failure();2490 2491 auto bodyCB = [&](InsertPointTy allocaIP, InsertPointTy codegenIP) {2492 builder.restoreIP(codegenIP);2493 return convertOmpOpRegions(tgOp.getRegion(), "omp.taskgroup.region",2494 builder, moduleTranslation)2495 .takeError();2496 };2497 2498 InsertPointTy allocaIP = findAllocaInsertPoint(builder, moduleTranslation);2499 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);2500 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =2501 moduleTranslation.getOpenMPBuilder()->createTaskgroup(ompLoc, allocaIP,2502 bodyCB);2503 2504 if (failed(handleError(afterIP, *tgOp)))2505 return failure();2506 2507 builder.restoreIP(*afterIP);2508 return success();2509}2510 2511static LogicalResult2512convertOmpTaskwaitOp(omp::TaskwaitOp twOp, llvm::IRBuilderBase &builder,2513 LLVM::ModuleTranslation &moduleTranslation) {2514 if (failed(checkImplementationStatus(*twOp)))2515 return failure();2516 2517 moduleTranslation.getOpenMPBuilder()->createTaskwait(builder.saveIP());2518 return success();2519}2520 2521/// Converts an OpenMP workshare loop into LLVM IR using OpenMPIRBuilder.2522static LogicalResult2523convertOmpWsloop(Operation &opInst, llvm::IRBuilderBase &builder,2524 LLVM::ModuleTranslation &moduleTranslation) {2525 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();2526 auto wsloopOp = cast<omp::WsloopOp>(opInst);2527 if (failed(checkImplementationStatus(opInst)))2528 return failure();2529 2530 auto loopOp = cast<omp::LoopNestOp>(wsloopOp.getWrappedLoop());2531 llvm::ArrayRef<bool> isByRef = getIsByRef(wsloopOp.getReductionByref());2532 assert(isByRef.size() == wsloopOp.getNumReductionVars());2533 2534 // Static is the default.2535 auto schedule =2536 wsloopOp.getScheduleKind().value_or(omp::ClauseScheduleKind::Static);2537 2538 // Find the loop configuration.2539 llvm::Value *step = moduleTranslation.lookupValue(loopOp.getLoopSteps()[0]);2540 llvm::Type *ivType = step->getType();2541 llvm::Value *chunk = nullptr;2542 if (wsloopOp.getScheduleChunk()) {2543 llvm::Value *chunkVar =2544 moduleTranslation.lookupValue(wsloopOp.getScheduleChunk());2545 chunk = builder.CreateSExtOrTrunc(chunkVar, ivType);2546 }2547 2548 omp::DistributeOp distributeOp = nullptr;2549 llvm::Value *distScheduleChunk = nullptr;2550 bool hasDistSchedule = false;2551 if (llvm::isa_and_present<omp::DistributeOp>(opInst.getParentOp())) {2552 distributeOp = cast<omp::DistributeOp>(opInst.getParentOp());2553 hasDistSchedule = distributeOp.getDistScheduleStatic();2554 if (distributeOp.getDistScheduleChunkSize()) {2555 llvm::Value *chunkVar = moduleTranslation.lookupValue(2556 distributeOp.getDistScheduleChunkSize());2557 distScheduleChunk = builder.CreateSExtOrTrunc(chunkVar, ivType);2558 }2559 }2560 2561 PrivateVarsInfo privateVarsInfo(wsloopOp);2562 2563 SmallVector<omp::DeclareReductionOp> reductionDecls;2564 collectReductionDecls(wsloopOp, reductionDecls);2565 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =2566 findAllocaInsertPoint(builder, moduleTranslation);2567 2568 SmallVector<llvm::Value *> privateReductionVariables(2569 wsloopOp.getNumReductionVars());2570 2571 llvm::Expected<llvm::BasicBlock *> afterAllocas = allocatePrivateVars(2572 builder, moduleTranslation, privateVarsInfo, allocaIP);2573 if (handleError(afterAllocas, opInst).failed())2574 return failure();2575 2576 DenseMap<Value, llvm::Value *> reductionVariableMap;2577 2578 MutableArrayRef<BlockArgument> reductionArgs =2579 cast<omp::BlockArgOpenMPOpInterface>(opInst).getReductionBlockArgs();2580 2581 SmallVector<DeferredStore> deferredStores;2582 2583 if (failed(allocReductionVars(wsloopOp, reductionArgs, builder,2584 moduleTranslation, allocaIP, reductionDecls,2585 privateReductionVariables, reductionVariableMap,2586 deferredStores, isByRef)))2587 return failure();2588 2589 if (handleError(initPrivateVars(builder, moduleTranslation, privateVarsInfo),2590 opInst)2591 .failed())2592 return failure();2593 2594 if (failed(copyFirstPrivateVars(2595 wsloopOp, builder, moduleTranslation, privateVarsInfo.mlirVars,2596 privateVarsInfo.llvmVars, privateVarsInfo.privatizers,2597 wsloopOp.getPrivateNeedsBarrier())))2598 return failure();2599 2600 assert(afterAllocas.get()->getSinglePredecessor());2601 if (failed(initReductionVars(wsloopOp, reductionArgs, builder,2602 moduleTranslation,2603 afterAllocas.get()->getSinglePredecessor(),2604 reductionDecls, privateReductionVariables,2605 reductionVariableMap, isByRef, deferredStores)))2606 return failure();2607 2608 // TODO: Handle doacross loops when the ordered clause has a parameter.2609 bool isOrdered = wsloopOp.getOrdered().has_value();2610 std::optional<omp::ScheduleModifier> scheduleMod = wsloopOp.getScheduleMod();2611 bool isSimd = wsloopOp.getScheduleSimd();2612 bool loopNeedsBarrier = !wsloopOp.getNowait();2613 2614 // The only legal way for the direct parent to be omp.distribute is that this2615 // represents 'distribute parallel do'. Otherwise, this is a regular2616 // worksharing loop.2617 llvm::omp::WorksharingLoopType workshareLoopType =2618 llvm::isa_and_present<omp::DistributeOp>(opInst.getParentOp())2619 ? llvm::omp::WorksharingLoopType::DistributeForStaticLoop2620 : llvm::omp::WorksharingLoopType::ForStaticLoop;2621 2622 SmallVector<llvm::BranchInst *> cancelTerminators;2623 pushCancelFinalizationCB(cancelTerminators, builder, *ompBuilder, wsloopOp,2624 llvm::omp::Directive::OMPD_for);2625 2626 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);2627 2628 // Initialize linear variables and linear step2629 LinearClauseProcessor linearClauseProcessor;2630 if (!wsloopOp.getLinearVars().empty()) {2631 for (mlir::Value linearVar : wsloopOp.getLinearVars())2632 linearClauseProcessor.createLinearVar(builder, moduleTranslation,2633 linearVar);2634 for (mlir::Value linearStep : wsloopOp.getLinearStepVars())2635 linearClauseProcessor.initLinearStep(moduleTranslation, linearStep);2636 }2637 2638 llvm::Expected<llvm::BasicBlock *> regionBlock = convertOmpOpRegions(2639 wsloopOp.getRegion(), "omp.wsloop.region", builder, moduleTranslation);2640 2641 if (failed(handleError(regionBlock, opInst)))2642 return failure();2643 2644 llvm::CanonicalLoopInfo *loopInfo = findCurrentLoopInfo(moduleTranslation);2645 2646 // Emit Initialization and Update IR for linear variables2647 if (!wsloopOp.getLinearVars().empty()) {2648 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterBarrierIP =2649 linearClauseProcessor.initLinearVar(builder, moduleTranslation,2650 loopInfo->getPreheader());2651 if (failed(handleError(afterBarrierIP, *loopOp)))2652 return failure();2653 builder.restoreIP(*afterBarrierIP);2654 linearClauseProcessor.updateLinearVar(builder, loopInfo->getBody(),2655 loopInfo->getIndVar());2656 linearClauseProcessor.outlineLinearFinalizationBB(builder,2657 loopInfo->getExit());2658 }2659 2660 builder.SetInsertPoint(*regionBlock, (*regionBlock)->begin());2661 2662 // Check if we can generate no-loop kernel2663 bool noLoopMode = false;2664 omp::TargetOp targetOp = wsloopOp->getParentOfType<mlir::omp::TargetOp>();2665 if (targetOp) {2666 Operation *targetCapturedOp = targetOp.getInnermostCapturedOmpOp();2667 // We need this check because, without it, noLoopMode would be set to true2668 // for every omp.wsloop nested inside a no-loop SPMD target region, even if2669 // that loop is not the top-level SPMD one.2670 if (loopOp == targetCapturedOp) {2671 omp::TargetRegionFlags kernelFlags =2672 targetOp.getKernelExecFlags(targetCapturedOp);2673 if (omp::bitEnumContainsAll(kernelFlags,2674 omp::TargetRegionFlags::spmd |2675 omp::TargetRegionFlags::no_loop) &&2676 !omp::bitEnumContainsAny(kernelFlags,2677 omp::TargetRegionFlags::generic))2678 noLoopMode = true;2679 }2680 }2681 2682 llvm::OpenMPIRBuilder::InsertPointOrErrorTy wsloopIP =2683 ompBuilder->applyWorkshareLoop(2684 ompLoc.DL, loopInfo, allocaIP, loopNeedsBarrier,2685 convertToScheduleKind(schedule), chunk, isSimd,2686 scheduleMod == omp::ScheduleModifier::monotonic,2687 scheduleMod == omp::ScheduleModifier::nonmonotonic, isOrdered,2688 workshareLoopType, noLoopMode, hasDistSchedule, distScheduleChunk);2689 2690 if (failed(handleError(wsloopIP, opInst)))2691 return failure();2692 2693 // Emit finalization and in-place rewrites for linear vars.2694 if (!wsloopOp.getLinearVars().empty()) {2695 llvm::OpenMPIRBuilder::InsertPointTy oldIP = builder.saveIP();2696 assert(loopInfo->getLastIter() &&2697 "`lastiter` in CanonicalLoopInfo is nullptr");2698 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterBarrierIP =2699 linearClauseProcessor.finalizeLinearVar(builder, moduleTranslation,2700 loopInfo->getLastIter());2701 if (failed(handleError(afterBarrierIP, *loopOp)))2702 return failure();2703 for (size_t index = 0; index < wsloopOp.getLinearVars().size(); index++)2704 linearClauseProcessor.rewriteInPlace(builder, "omp.loop_nest.region",2705 index);2706 builder.restoreIP(oldIP);2707 }2708 2709 // Set the correct branch target for task cancellation2710 popCancelFinalizationCB(cancelTerminators, *ompBuilder, wsloopIP.get());2711 2712 // Process the reductions if required.2713 if (failed(createReductionsAndCleanup(2714 wsloopOp, builder, moduleTranslation, allocaIP, reductionDecls,2715 privateReductionVariables, isByRef, wsloopOp.getNowait(),2716 /*isTeamsReduction=*/false)))2717 return failure();2718 2719 return cleanupPrivateVars(builder, moduleTranslation, wsloopOp.getLoc(),2720 privateVarsInfo.llvmVars,2721 privateVarsInfo.privatizers);2722}2723 2724/// Converts the OpenMP parallel operation to LLVM IR.2725static LogicalResult2726convertOmpParallel(omp::ParallelOp opInst, llvm::IRBuilderBase &builder,2727 LLVM::ModuleTranslation &moduleTranslation) {2728 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;2729 ArrayRef<bool> isByRef = getIsByRef(opInst.getReductionByref());2730 assert(isByRef.size() == opInst.getNumReductionVars());2731 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();2732 bool isCancellable = constructIsCancellable(opInst);2733 2734 if (failed(checkImplementationStatus(*opInst)))2735 return failure();2736 2737 PrivateVarsInfo privateVarsInfo(opInst);2738 2739 // Collect reduction declarations2740 SmallVector<omp::DeclareReductionOp> reductionDecls;2741 collectReductionDecls(opInst, reductionDecls);2742 SmallVector<llvm::Value *> privateReductionVariables(2743 opInst.getNumReductionVars());2744 SmallVector<DeferredStore> deferredStores;2745 2746 auto bodyGenCB = [&](InsertPointTy allocaIP,2747 InsertPointTy codeGenIP) -> llvm::Error {2748 llvm::Expected<llvm::BasicBlock *> afterAllocas = allocatePrivateVars(2749 builder, moduleTranslation, privateVarsInfo, allocaIP);2750 if (handleError(afterAllocas, *opInst).failed())2751 return llvm::make_error<PreviouslyReportedError>();2752 2753 // Allocate reduction vars2754 DenseMap<Value, llvm::Value *> reductionVariableMap;2755 2756 MutableArrayRef<BlockArgument> reductionArgs =2757 cast<omp::BlockArgOpenMPOpInterface>(*opInst).getReductionBlockArgs();2758 2759 allocaIP =2760 InsertPointTy(allocaIP.getBlock(),2761 allocaIP.getBlock()->getTerminator()->getIterator());2762 2763 if (failed(allocReductionVars(2764 opInst, reductionArgs, builder, moduleTranslation, allocaIP,2765 reductionDecls, privateReductionVariables, reductionVariableMap,2766 deferredStores, isByRef)))2767 return llvm::make_error<PreviouslyReportedError>();2768 2769 assert(afterAllocas.get()->getSinglePredecessor());2770 builder.restoreIP(codeGenIP);2771 2772 if (handleError(2773 initPrivateVars(builder, moduleTranslation, privateVarsInfo),2774 *opInst)2775 .failed())2776 return llvm::make_error<PreviouslyReportedError>();2777 2778 if (failed(copyFirstPrivateVars(2779 opInst, builder, moduleTranslation, privateVarsInfo.mlirVars,2780 privateVarsInfo.llvmVars, privateVarsInfo.privatizers,2781 opInst.getPrivateNeedsBarrier())))2782 return llvm::make_error<PreviouslyReportedError>();2783 2784 if (failed(2785 initReductionVars(opInst, reductionArgs, builder, moduleTranslation,2786 afterAllocas.get()->getSinglePredecessor(),2787 reductionDecls, privateReductionVariables,2788 reductionVariableMap, isByRef, deferredStores)))2789 return llvm::make_error<PreviouslyReportedError>();2790 2791 // Save the alloca insertion point on ModuleTranslation stack for use in2792 // nested regions.2793 LLVM::ModuleTranslation::SaveStack<OpenMPAllocaStackFrame> frame(2794 moduleTranslation, allocaIP);2795 2796 // ParallelOp has only one region associated with it.2797 llvm::Expected<llvm::BasicBlock *> regionBlock = convertOmpOpRegions(2798 opInst.getRegion(), "omp.par.region", builder, moduleTranslation);2799 if (!regionBlock)2800 return regionBlock.takeError();2801 2802 // Process the reductions if required.2803 if (opInst.getNumReductionVars() > 0) {2804 // Collect reduction info2805 SmallVector<OwningReductionGen> owningReductionGens;2806 SmallVector<OwningAtomicReductionGen> owningAtomicReductionGens;2807 SmallVector<OwningDataPtrPtrReductionGen>2808 owningReductionGenRefDataPtrGens;2809 SmallVector<llvm::OpenMPIRBuilder::ReductionInfo, 2> reductionInfos;2810 collectReductionInfo(opInst, builder, moduleTranslation, reductionDecls,2811 owningReductionGens, owningAtomicReductionGens,2812 owningReductionGenRefDataPtrGens,2813 privateReductionVariables, reductionInfos, isByRef);2814 2815 // Move to region cont block2816 builder.SetInsertPoint((*regionBlock)->getTerminator());2817 2818 // Generate reductions from info2819 llvm::UnreachableInst *tempTerminator = builder.CreateUnreachable();2820 builder.SetInsertPoint(tempTerminator);2821 2822 llvm::OpenMPIRBuilder::InsertPointOrErrorTy contInsertPoint =2823 ompBuilder->createReductions(2824 builder.saveIP(), allocaIP, reductionInfos, isByRef,2825 /*IsNoWait=*/false, /*IsTeamsReduction=*/false);2826 if (!contInsertPoint)2827 return contInsertPoint.takeError();2828 2829 if (!contInsertPoint->getBlock())2830 return llvm::make_error<PreviouslyReportedError>();2831 2832 tempTerminator->eraseFromParent();2833 builder.restoreIP(*contInsertPoint);2834 }2835 2836 return llvm::Error::success();2837 };2838 2839 auto privCB = [](InsertPointTy allocaIP, InsertPointTy codeGenIP,2840 llvm::Value &, llvm::Value &val, llvm::Value *&replVal) {2841 // tell OpenMPIRBuilder not to do anything. We handled Privatisation in2842 // bodyGenCB.2843 replVal = &val;2844 return codeGenIP;2845 };2846 2847 // TODO: Perform finalization actions for variables. This has to be2848 // called for variables which have destructors/finalizers.2849 auto finiCB = [&](InsertPointTy codeGenIP) -> llvm::Error {2850 InsertPointTy oldIP = builder.saveIP();2851 builder.restoreIP(codeGenIP);2852 2853 // if the reduction has a cleanup region, inline it here to finalize the2854 // reduction variables2855 SmallVector<Region *> reductionCleanupRegions;2856 llvm::transform(reductionDecls, std::back_inserter(reductionCleanupRegions),2857 [](omp::DeclareReductionOp reductionDecl) {2858 return &reductionDecl.getCleanupRegion();2859 });2860 if (failed(inlineOmpRegionCleanup(2861 reductionCleanupRegions, privateReductionVariables,2862 moduleTranslation, builder, "omp.reduction.cleanup")))2863 return llvm::createStringError(2864 "failed to inline `cleanup` region of `omp.declare_reduction`");2865 2866 if (failed(cleanupPrivateVars(builder, moduleTranslation, opInst.getLoc(),2867 privateVarsInfo.llvmVars,2868 privateVarsInfo.privatizers)))2869 return llvm::make_error<PreviouslyReportedError>();2870 2871 // If we could be performing cancellation, add the cancellation barrier on2872 // the way out of the outlined region.2873 if (isCancellable) {2874 auto IPOrErr = ompBuilder->createBarrier(2875 llvm::OpenMPIRBuilder::LocationDescription(builder),2876 llvm::omp::Directive::OMPD_unknown,2877 /* ForceSimpleCall */ false,2878 /* CheckCancelFlag */ false);2879 if (!IPOrErr)2880 return IPOrErr.takeError();2881 }2882 2883 builder.restoreIP(oldIP);2884 return llvm::Error::success();2885 };2886 2887 llvm::Value *ifCond = nullptr;2888 if (auto ifVar = opInst.getIfExpr())2889 ifCond = moduleTranslation.lookupValue(ifVar);2890 llvm::Value *numThreads = nullptr;2891 if (auto numThreadsVar = opInst.getNumThreads())2892 numThreads = moduleTranslation.lookupValue(numThreadsVar);2893 auto pbKind = llvm::omp::OMP_PROC_BIND_default;2894 if (auto bind = opInst.getProcBindKind())2895 pbKind = getProcBindKind(*bind);2896 2897 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =2898 findAllocaInsertPoint(builder, moduleTranslation);2899 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);2900 2901 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =2902 ompBuilder->createParallel(ompLoc, allocaIP, bodyGenCB, privCB, finiCB,2903 ifCond, numThreads, pbKind, isCancellable);2904 2905 if (failed(handleError(afterIP, *opInst)))2906 return failure();2907 2908 builder.restoreIP(*afterIP);2909 return success();2910}2911 2912/// Convert Order attribute to llvm::omp::OrderKind.2913static llvm::omp::OrderKind2914convertOrderKind(std::optional<omp::ClauseOrderKind> o) {2915 if (!o)2916 return llvm::omp::OrderKind::OMP_ORDER_unknown;2917 switch (*o) {2918 case omp::ClauseOrderKind::Concurrent:2919 return llvm::omp::OrderKind::OMP_ORDER_concurrent;2920 }2921 llvm_unreachable("Unknown ClauseOrderKind kind");2922}2923 2924/// Converts an OpenMP simd loop into LLVM IR using OpenMPIRBuilder.2925static LogicalResult2926convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,2927 LLVM::ModuleTranslation &moduleTranslation) {2928 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();2929 auto simdOp = cast<omp::SimdOp>(opInst);2930 2931 if (failed(checkImplementationStatus(opInst)))2932 return failure();2933 2934 PrivateVarsInfo privateVarsInfo(simdOp);2935 2936 MutableArrayRef<BlockArgument> reductionArgs =2937 cast<omp::BlockArgOpenMPOpInterface>(opInst).getReductionBlockArgs();2938 DenseMap<Value, llvm::Value *> reductionVariableMap;2939 SmallVector<llvm::Value *> privateReductionVariables(2940 simdOp.getNumReductionVars());2941 SmallVector<DeferredStore> deferredStores;2942 SmallVector<omp::DeclareReductionOp> reductionDecls;2943 collectReductionDecls(simdOp, reductionDecls);2944 llvm::ArrayRef<bool> isByRef = getIsByRef(simdOp.getReductionByref());2945 assert(isByRef.size() == simdOp.getNumReductionVars());2946 2947 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =2948 findAllocaInsertPoint(builder, moduleTranslation);2949 2950 llvm::Expected<llvm::BasicBlock *> afterAllocas = allocatePrivateVars(2951 builder, moduleTranslation, privateVarsInfo, allocaIP);2952 if (handleError(afterAllocas, opInst).failed())2953 return failure();2954 2955 if (failed(allocReductionVars(simdOp, reductionArgs, builder,2956 moduleTranslation, allocaIP, reductionDecls,2957 privateReductionVariables, reductionVariableMap,2958 deferredStores, isByRef)))2959 return failure();2960 2961 if (handleError(initPrivateVars(builder, moduleTranslation, privateVarsInfo),2962 opInst)2963 .failed())2964 return failure();2965 2966 // No call to copyFirstPrivateVars because FIRSTPRIVATE is not allowed for2967 // SIMD.2968 2969 assert(afterAllocas.get()->getSinglePredecessor());2970 if (failed(initReductionVars(simdOp, reductionArgs, builder,2971 moduleTranslation,2972 afterAllocas.get()->getSinglePredecessor(),2973 reductionDecls, privateReductionVariables,2974 reductionVariableMap, isByRef, deferredStores)))2975 return failure();2976 2977 llvm::ConstantInt *simdlen = nullptr;2978 if (std::optional<uint64_t> simdlenVar = simdOp.getSimdlen())2979 simdlen = builder.getInt64(simdlenVar.value());2980 2981 llvm::ConstantInt *safelen = nullptr;2982 if (std::optional<uint64_t> safelenVar = simdOp.getSafelen())2983 safelen = builder.getInt64(safelenVar.value());2984 2985 llvm::MapVector<llvm::Value *, llvm::Value *> alignedVars;2986 llvm::omp::OrderKind order = convertOrderKind(simdOp.getOrder());2987 2988 llvm::BasicBlock *sourceBlock = builder.GetInsertBlock();2989 std::optional<ArrayAttr> alignmentValues = simdOp.getAlignments();2990 mlir::OperandRange operands = simdOp.getAlignedVars();2991 for (size_t i = 0; i < operands.size(); ++i) {2992 llvm::Value *alignment = nullptr;2993 llvm::Value *llvmVal = moduleTranslation.lookupValue(operands[i]);2994 llvm::Type *ty = llvmVal->getType();2995 2996 auto intAttr = cast<IntegerAttr>((*alignmentValues)[i]);2997 alignment = builder.getInt64(intAttr.getInt());2998 assert(ty->isPointerTy() && "Invalid type for aligned variable");2999 assert(alignment && "Invalid alignment value");3000 3001 // Check if the alignment value is not a power of 2. If so, skip emitting3002 // alignment.3003 if (!intAttr.getValue().isPowerOf2())3004 continue;3005 3006 auto curInsert = builder.saveIP();3007 builder.SetInsertPoint(sourceBlock);3008 llvmVal = builder.CreateLoad(ty, llvmVal);3009 builder.restoreIP(curInsert);3010 alignedVars[llvmVal] = alignment;3011 }3012 3013 llvm::Expected<llvm::BasicBlock *> regionBlock = convertOmpOpRegions(3014 simdOp.getRegion(), "omp.simd.region", builder, moduleTranslation);3015 3016 if (failed(handleError(regionBlock, opInst)))3017 return failure();3018 3019 builder.SetInsertPoint(*regionBlock, (*regionBlock)->begin());3020 llvm::CanonicalLoopInfo *loopInfo = findCurrentLoopInfo(moduleTranslation);3021 ompBuilder->applySimd(loopInfo, alignedVars,3022 simdOp.getIfExpr()3023 ? moduleTranslation.lookupValue(simdOp.getIfExpr())3024 : nullptr,3025 order, simdlen, safelen);3026 3027 // We now need to reduce the per-simd-lane reduction variable into the3028 // original variable. This works a bit differently to other reductions (e.g.3029 // wsloop) because we don't need to call into the OpenMP runtime to handle3030 // threads: everything happened in this one thread.3031 for (auto [i, tuple] : llvm::enumerate(3032 llvm::zip(reductionDecls, isByRef, simdOp.getReductionVars(),3033 privateReductionVariables))) {3034 auto [decl, byRef, reductionVar, privateReductionVar] = tuple;3035 3036 OwningReductionGen gen = makeReductionGen(decl, builder, moduleTranslation);3037 llvm::Value *originalVariable = moduleTranslation.lookupValue(reductionVar);3038 llvm::Type *reductionType = moduleTranslation.convertType(decl.getType());3039 3040 // We have one less load for by-ref case because that load is now inside of3041 // the reduction region.3042 llvm::Value *redValue = originalVariable;3043 if (!byRef)3044 redValue =3045 builder.CreateLoad(reductionType, redValue, "red.value." + Twine(i));3046 llvm::Value *privateRedValue = builder.CreateLoad(3047 reductionType, privateReductionVar, "red.private.value." + Twine(i));3048 llvm::Value *reduced;3049 3050 auto res = gen(builder.saveIP(), redValue, privateRedValue, reduced);3051 if (failed(handleError(res, opInst)))3052 return failure();3053 builder.restoreIP(res.get());3054 3055 // For by-ref case, the store is inside of the reduction region.3056 if (!byRef)3057 builder.CreateStore(reduced, originalVariable);3058 }3059 3060 // After the construct, deallocate private reduction variables.3061 SmallVector<Region *> reductionRegions;3062 llvm::transform(reductionDecls, std::back_inserter(reductionRegions),3063 [](omp::DeclareReductionOp reductionDecl) {3064 return &reductionDecl.getCleanupRegion();3065 });3066 if (failed(inlineOmpRegionCleanup(reductionRegions, privateReductionVariables,3067 moduleTranslation, builder,3068 "omp.reduction.cleanup")))3069 return failure();3070 3071 return cleanupPrivateVars(builder, moduleTranslation, simdOp.getLoc(),3072 privateVarsInfo.llvmVars,3073 privateVarsInfo.privatizers);3074}3075 3076/// Converts an OpenMP loop nest into LLVM IR using OpenMPIRBuilder.3077static LogicalResult3078convertOmpLoopNest(Operation &opInst, llvm::IRBuilderBase &builder,3079 LLVM::ModuleTranslation &moduleTranslation) {3080 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3081 auto loopOp = cast<omp::LoopNestOp>(opInst);3082 3083 // Set up the source location value for OpenMP runtime.3084 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);3085 3086 // Generator of the canonical loop body.3087 SmallVector<llvm::CanonicalLoopInfo *> loopInfos;3088 SmallVector<llvm::OpenMPIRBuilder::InsertPointTy> bodyInsertPoints;3089 auto bodyGen = [&](llvm::OpenMPIRBuilder::InsertPointTy ip,3090 llvm::Value *iv) -> llvm::Error {3091 // Make sure further conversions know about the induction variable.3092 moduleTranslation.mapValue(3093 loopOp.getRegion().front().getArgument(loopInfos.size()), iv);3094 3095 // Capture the body insertion point for use in nested loops. BodyIP of the3096 // CanonicalLoopInfo always points to the beginning of the entry block of3097 // the body.3098 bodyInsertPoints.push_back(ip);3099 3100 if (loopInfos.size() != loopOp.getNumLoops() - 1)3101 return llvm::Error::success();3102 3103 // Convert the body of the loop.3104 builder.restoreIP(ip);3105 llvm::Expected<llvm::BasicBlock *> regionBlock = convertOmpOpRegions(3106 loopOp.getRegion(), "omp.loop_nest.region", builder, moduleTranslation);3107 if (!regionBlock)3108 return regionBlock.takeError();3109 3110 builder.SetInsertPoint(*regionBlock, (*regionBlock)->begin());3111 return llvm::Error::success();3112 };3113 3114 // Delegate actual loop construction to the OpenMP IRBuilder.3115 // TODO: this currently assumes omp.loop_nest is semantically similar to SCF3116 // loop, i.e. it has a positive step, uses signed integer semantics.3117 // Reconsider this code when the nested loop operation clearly supports more3118 // cases.3119 for (unsigned i = 0, e = loopOp.getNumLoops(); i < e; ++i) {3120 llvm::Value *lowerBound =3121 moduleTranslation.lookupValue(loopOp.getLoopLowerBounds()[i]);3122 llvm::Value *upperBound =3123 moduleTranslation.lookupValue(loopOp.getLoopUpperBounds()[i]);3124 llvm::Value *step = moduleTranslation.lookupValue(loopOp.getLoopSteps()[i]);3125 3126 // Make sure loop trip count are emitted in the preheader of the outermost3127 // loop at the latest so that they are all available for the new collapsed3128 // loop will be created below.3129 llvm::OpenMPIRBuilder::LocationDescription loc = ompLoc;3130 llvm::OpenMPIRBuilder::InsertPointTy computeIP = ompLoc.IP;3131 if (i != 0) {3132 loc = llvm::OpenMPIRBuilder::LocationDescription(bodyInsertPoints.back(),3133 ompLoc.DL);3134 computeIP = loopInfos.front()->getPreheaderIP();3135 }3136 3137 llvm::Expected<llvm::CanonicalLoopInfo *> loopResult =3138 ompBuilder->createCanonicalLoop(3139 loc, bodyGen, lowerBound, upperBound, step,3140 /*IsSigned=*/true, loopOp.getLoopInclusive(), computeIP);3141 3142 if (failed(handleError(loopResult, *loopOp)))3143 return failure();3144 3145 loopInfos.push_back(*loopResult);3146 }3147 3148 llvm::OpenMPIRBuilder::InsertPointTy afterIP =3149 loopInfos.front()->getAfterIP();3150 3151 // Do tiling.3152 if (const auto &tiles = loopOp.getTileSizes()) {3153 llvm::Type *ivType = loopInfos.front()->getIndVarType();3154 SmallVector<llvm::Value *> tileSizes;3155 3156 for (auto tile : tiles.value()) {3157 llvm::Value *tileVal = llvm::ConstantInt::get(ivType, tile);3158 tileSizes.push_back(tileVal);3159 }3160 3161 std::vector<llvm::CanonicalLoopInfo *> newLoops =3162 ompBuilder->tileLoops(ompLoc.DL, loopInfos, tileSizes);3163 3164 // Update afterIP to get the correct insertion point after3165 // tiling.3166 llvm::BasicBlock *afterBB = newLoops.front()->getAfter();3167 llvm::BasicBlock *afterAfterBB = afterBB->getSingleSuccessor();3168 afterIP = {afterAfterBB, afterAfterBB->begin()};3169 3170 // Update the loop infos.3171 loopInfos.clear();3172 for (const auto &newLoop : newLoops)3173 loopInfos.push_back(newLoop);3174 } // Tiling done.3175 3176 // Do collapse.3177 const auto &numCollapse = loopOp.getCollapseNumLoops();3178 SmallVector<llvm::CanonicalLoopInfo *> collapseLoopInfos(3179 loopInfos.begin(), loopInfos.begin() + (numCollapse));3180 3181 auto newTopLoopInfo =3182 ompBuilder->collapseLoops(ompLoc.DL, collapseLoopInfos, {});3183 3184 assert(newTopLoopInfo && "New top loop information is missing");3185 moduleTranslation.stackWalk<OpenMPLoopInfoStackFrame>(3186 [&](OpenMPLoopInfoStackFrame &frame) {3187 frame.loopInfo = newTopLoopInfo;3188 return WalkResult::interrupt();3189 });3190 3191 // Continue building IR after the loop. Note that the LoopInfo returned by3192 // `collapseLoops` points inside the outermost loop and is intended for3193 // potential further loop transformations. Use the insertion point stored3194 // before collapsing loops instead.3195 builder.restoreIP(afterIP);3196 return success();3197}3198 3199/// Convert an omp.canonical_loop to LLVM-IR3200static LogicalResult3201convertOmpCanonicalLoopOp(omp::CanonicalLoopOp op, llvm::IRBuilderBase &builder,3202 LLVM::ModuleTranslation &moduleTranslation) {3203 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3204 3205 llvm::OpenMPIRBuilder::LocationDescription loopLoc(builder);3206 Value loopIV = op.getInductionVar();3207 Value loopTC = op.getTripCount();3208 3209 llvm::Value *llvmTC = moduleTranslation.lookupValue(loopTC);3210 3211 llvm::Expected<llvm::CanonicalLoopInfo *> llvmOrError =3212 ompBuilder->createCanonicalLoop(3213 loopLoc,3214 [&](llvm::OpenMPIRBuilder::InsertPointTy ip, llvm::Value *llvmIV) {3215 // Register the mapping of MLIR induction variable to LLVM-IR3216 // induction variable3217 moduleTranslation.mapValue(loopIV, llvmIV);3218 3219 builder.restoreIP(ip);3220 llvm::Expected<llvm::BasicBlock *> bodyGenStatus =3221 convertOmpOpRegions(op.getRegion(), "omp.loop.region", builder,3222 moduleTranslation);3223 3224 return bodyGenStatus.takeError();3225 },3226 llvmTC, "omp.loop");3227 if (!llvmOrError)3228 return op.emitError(llvm::toString(llvmOrError.takeError()));3229 3230 llvm::CanonicalLoopInfo *llvmCLI = *llvmOrError;3231 llvm::IRBuilderBase::InsertPoint afterIP = llvmCLI->getAfterIP();3232 builder.restoreIP(afterIP);3233 3234 // Register the mapping of MLIR loop to LLVM-IR OpenMPIRBuilder loop3235 if (Value cli = op.getCli())3236 moduleTranslation.mapOmpLoop(cli, llvmCLI);3237 3238 return success();3239}3240 3241/// Apply a `#pragma omp unroll` / "!$omp unroll" transformation using the3242/// OpenMPIRBuilder.3243static LogicalResult3244applyUnrollHeuristic(omp::UnrollHeuristicOp op, llvm::IRBuilderBase &builder,3245 LLVM::ModuleTranslation &moduleTranslation) {3246 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3247 3248 Value applyee = op.getApplyee();3249 assert(applyee && "Loop to apply unrolling on required");3250 3251 llvm::CanonicalLoopInfo *consBuilderCLI =3252 moduleTranslation.lookupOMPLoop(applyee);3253 llvm::OpenMPIRBuilder::LocationDescription loc(builder);3254 ompBuilder->unrollLoopHeuristic(loc.DL, consBuilderCLI);3255 3256 moduleTranslation.invalidateOmpLoop(applyee);3257 return success();3258}3259 3260/// Apply a `#pragma omp tile` / `!$omp tile` transformation using the3261/// OpenMPIRBuilder.3262static LogicalResult applyTile(omp::TileOp op, llvm::IRBuilderBase &builder,3263 LLVM::ModuleTranslation &moduleTranslation) {3264 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3265 llvm::OpenMPIRBuilder::LocationDescription loc(builder);3266 3267 SmallVector<llvm::CanonicalLoopInfo *> translatedLoops;3268 SmallVector<llvm::Value *> translatedSizes;3269 3270 for (Value size : op.getSizes()) {3271 llvm::Value *translatedSize = moduleTranslation.lookupValue(size);3272 assert(translatedSize &&3273 "sizes clause arguments must already be translated");3274 translatedSizes.push_back(translatedSize);3275 }3276 3277 for (Value applyee : op.getApplyees()) {3278 llvm::CanonicalLoopInfo *consBuilderCLI =3279 moduleTranslation.lookupOMPLoop(applyee);3280 assert(applyee && "Canonical loop must already been translated");3281 translatedLoops.push_back(consBuilderCLI);3282 }3283 3284 auto generatedLoops =3285 ompBuilder->tileLoops(loc.DL, translatedLoops, translatedSizes);3286 if (!op.getGeneratees().empty()) {3287 for (auto [mlirLoop, genLoop] :3288 zip_equal(op.getGeneratees(), generatedLoops))3289 moduleTranslation.mapOmpLoop(mlirLoop, genLoop);3290 }3291 3292 // CLIs can only be consumed once3293 for (Value applyee : op.getApplyees())3294 moduleTranslation.invalidateOmpLoop(applyee);3295 3296 return success();3297}3298 3299/// Convert an Atomic Ordering attribute to llvm::AtomicOrdering.3300static llvm::AtomicOrdering3301convertAtomicOrdering(std::optional<omp::ClauseMemoryOrderKind> ao) {3302 if (!ao)3303 return llvm::AtomicOrdering::Monotonic; // Default Memory Ordering3304 3305 switch (*ao) {3306 case omp::ClauseMemoryOrderKind::Seq_cst:3307 return llvm::AtomicOrdering::SequentiallyConsistent;3308 case omp::ClauseMemoryOrderKind::Acq_rel:3309 return llvm::AtomicOrdering::AcquireRelease;3310 case omp::ClauseMemoryOrderKind::Acquire:3311 return llvm::AtomicOrdering::Acquire;3312 case omp::ClauseMemoryOrderKind::Release:3313 return llvm::AtomicOrdering::Release;3314 case omp::ClauseMemoryOrderKind::Relaxed:3315 return llvm::AtomicOrdering::Monotonic;3316 }3317 llvm_unreachable("Unknown ClauseMemoryOrderKind kind");3318}3319 3320/// Convert omp.atomic.read operation to LLVM IR.3321static LogicalResult3322convertOmpAtomicRead(Operation &opInst, llvm::IRBuilderBase &builder,3323 LLVM::ModuleTranslation &moduleTranslation) {3324 auto readOp = cast<omp::AtomicReadOp>(opInst);3325 if (failed(checkImplementationStatus(opInst)))3326 return failure();3327 3328 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3329 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =3330 findAllocaInsertPoint(builder, moduleTranslation);3331 3332 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);3333 3334 llvm::AtomicOrdering AO = convertAtomicOrdering(readOp.getMemoryOrder());3335 llvm::Value *x = moduleTranslation.lookupValue(readOp.getX());3336 llvm::Value *v = moduleTranslation.lookupValue(readOp.getV());3337 3338 llvm::Type *elementType =3339 moduleTranslation.convertType(readOp.getElementType());3340 3341 llvm::OpenMPIRBuilder::AtomicOpValue V = {v, elementType, false, false};3342 llvm::OpenMPIRBuilder::AtomicOpValue X = {x, elementType, false, false};3343 builder.restoreIP(ompBuilder->createAtomicRead(ompLoc, X, V, AO, allocaIP));3344 return success();3345}3346 3347/// Converts an omp.atomic.write operation to LLVM IR.3348static LogicalResult3349convertOmpAtomicWrite(Operation &opInst, llvm::IRBuilderBase &builder,3350 LLVM::ModuleTranslation &moduleTranslation) {3351 auto writeOp = cast<omp::AtomicWriteOp>(opInst);3352 if (failed(checkImplementationStatus(opInst)))3353 return failure();3354 3355 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3356 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =3357 findAllocaInsertPoint(builder, moduleTranslation);3358 3359 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);3360 llvm::AtomicOrdering ao = convertAtomicOrdering(writeOp.getMemoryOrder());3361 llvm::Value *expr = moduleTranslation.lookupValue(writeOp.getExpr());3362 llvm::Value *dest = moduleTranslation.lookupValue(writeOp.getX());3363 llvm::Type *ty = moduleTranslation.convertType(writeOp.getExpr().getType());3364 llvm::OpenMPIRBuilder::AtomicOpValue x = {dest, ty, /*isSigned=*/false,3365 /*isVolatile=*/false};3366 builder.restoreIP(3367 ompBuilder->createAtomicWrite(ompLoc, x, expr, ao, allocaIP));3368 return success();3369}3370 3371/// Converts an LLVM dialect binary operation to the corresponding enum value3372/// for `atomicrmw` supported binary operation.3373static llvm::AtomicRMWInst::BinOp convertBinOpToAtomic(Operation &op) {3374 return llvm::TypeSwitch<Operation *, llvm::AtomicRMWInst::BinOp>(&op)3375 .Case([&](LLVM::AddOp) { return llvm::AtomicRMWInst::BinOp::Add; })3376 .Case([&](LLVM::SubOp) { return llvm::AtomicRMWInst::BinOp::Sub; })3377 .Case([&](LLVM::AndOp) { return llvm::AtomicRMWInst::BinOp::And; })3378 .Case([&](LLVM::OrOp) { return llvm::AtomicRMWInst::BinOp::Or; })3379 .Case([&](LLVM::XOrOp) { return llvm::AtomicRMWInst::BinOp::Xor; })3380 .Case([&](LLVM::UMaxOp) { return llvm::AtomicRMWInst::BinOp::UMax; })3381 .Case([&](LLVM::UMinOp) { return llvm::AtomicRMWInst::BinOp::UMin; })3382 .Case([&](LLVM::FAddOp) { return llvm::AtomicRMWInst::BinOp::FAdd; })3383 .Case([&](LLVM::FSubOp) { return llvm::AtomicRMWInst::BinOp::FSub; })3384 .Default(llvm::AtomicRMWInst::BinOp::BAD_BINOP);3385}3386 3387static void extractAtomicControlFlags(omp::AtomicUpdateOp atomicUpdateOp,3388 bool &isIgnoreDenormalMode,3389 bool &isFineGrainedMemory,3390 bool &isRemoteMemory) {3391 isIgnoreDenormalMode = false;3392 isFineGrainedMemory = false;3393 isRemoteMemory = false;3394 if (atomicUpdateOp &&3395 atomicUpdateOp->hasAttr(atomicUpdateOp.getAtomicControlAttrName())) {3396 mlir::omp::AtomicControlAttr atomicControlAttr =3397 atomicUpdateOp.getAtomicControlAttr();3398 isIgnoreDenormalMode = atomicControlAttr.getIgnoreDenormalMode();3399 isFineGrainedMemory = atomicControlAttr.getFineGrainedMemory();3400 isRemoteMemory = atomicControlAttr.getRemoteMemory();3401 }3402}3403 3404/// Converts an OpenMP atomic update operation using OpenMPIRBuilder.3405static LogicalResult3406convertOmpAtomicUpdate(omp::AtomicUpdateOp &opInst,3407 llvm::IRBuilderBase &builder,3408 LLVM::ModuleTranslation &moduleTranslation) {3409 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3410 if (failed(checkImplementationStatus(*opInst)))3411 return failure();3412 3413 // Convert values and types.3414 auto &innerOpList = opInst.getRegion().front().getOperations();3415 bool isXBinopExpr{false};3416 llvm::AtomicRMWInst::BinOp binop;3417 mlir::Value mlirExpr;3418 llvm::Value *llvmExpr = nullptr;3419 llvm::Value *llvmX = nullptr;3420 llvm::Type *llvmXElementType = nullptr;3421 if (innerOpList.size() == 2) {3422 // The two operations here are the update and the terminator.3423 // Since we can identify the update operation, there is a possibility3424 // that we can generate the atomicrmw instruction.3425 mlir::Operation &innerOp = *opInst.getRegion().front().begin();3426 if (!llvm::is_contained(innerOp.getOperands(),3427 opInst.getRegion().getArgument(0))) {3428 return opInst.emitError("no atomic update operation with region argument"3429 " as operand found inside atomic.update region");3430 }3431 binop = convertBinOpToAtomic(innerOp);3432 isXBinopExpr = innerOp.getOperand(0) == opInst.getRegion().getArgument(0);3433 mlirExpr = (isXBinopExpr ? innerOp.getOperand(1) : innerOp.getOperand(0));3434 llvmExpr = moduleTranslation.lookupValue(mlirExpr);3435 } else {3436 // Since the update region includes more than one operation3437 // we will resort to generating a cmpxchg loop.3438 binop = llvm::AtomicRMWInst::BinOp::BAD_BINOP;3439 }3440 llvmX = moduleTranslation.lookupValue(opInst.getX());3441 llvmXElementType = moduleTranslation.convertType(3442 opInst.getRegion().getArgument(0).getType());3443 llvm::OpenMPIRBuilder::AtomicOpValue llvmAtomicX = {llvmX, llvmXElementType,3444 /*isSigned=*/false,3445 /*isVolatile=*/false};3446 3447 llvm::AtomicOrdering atomicOrdering =3448 convertAtomicOrdering(opInst.getMemoryOrder());3449 3450 // Generate update code.3451 auto updateFn =3452 [&opInst, &moduleTranslation](3453 llvm::Value *atomicx,3454 llvm::IRBuilder<> &builder) -> llvm::Expected<llvm::Value *> {3455 Block &bb = *opInst.getRegion().begin();3456 moduleTranslation.mapValue(*opInst.getRegion().args_begin(), atomicx);3457 moduleTranslation.mapBlock(&bb, builder.GetInsertBlock());3458 if (failed(moduleTranslation.convertBlock(bb, true, builder)))3459 return llvm::make_error<PreviouslyReportedError>();3460 3461 omp::YieldOp yieldop = dyn_cast<omp::YieldOp>(bb.getTerminator());3462 assert(yieldop && yieldop.getResults().size() == 1 &&3463 "terminator must be omp.yield op and it must have exactly one "3464 "argument");3465 return moduleTranslation.lookupValue(yieldop.getResults()[0]);3466 };3467 3468 bool isIgnoreDenormalMode;3469 bool isFineGrainedMemory;3470 bool isRemoteMemory;3471 extractAtomicControlFlags(opInst, isIgnoreDenormalMode, isFineGrainedMemory,3472 isRemoteMemory);3473 // Handle ambiguous alloca, if any.3474 auto allocaIP = findAllocaInsertPoint(builder, moduleTranslation);3475 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);3476 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =3477 ompBuilder->createAtomicUpdate(ompLoc, allocaIP, llvmAtomicX, llvmExpr,3478 atomicOrdering, binop, updateFn,3479 isXBinopExpr, isIgnoreDenormalMode,3480 isFineGrainedMemory, isRemoteMemory);3481 3482 if (failed(handleError(afterIP, *opInst)))3483 return failure();3484 3485 builder.restoreIP(*afterIP);3486 return success();3487}3488 3489static LogicalResult3490convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,3491 llvm::IRBuilderBase &builder,3492 LLVM::ModuleTranslation &moduleTranslation) {3493 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3494 if (failed(checkImplementationStatus(*atomicCaptureOp)))3495 return failure();3496 3497 mlir::Value mlirExpr;3498 bool isXBinopExpr = false, isPostfixUpdate = false;3499 llvm::AtomicRMWInst::BinOp binop = llvm::AtomicRMWInst::BinOp::BAD_BINOP;3500 3501 omp::AtomicUpdateOp atomicUpdateOp = atomicCaptureOp.getAtomicUpdateOp();3502 omp::AtomicWriteOp atomicWriteOp = atomicCaptureOp.getAtomicWriteOp();3503 3504 assert((atomicUpdateOp || atomicWriteOp) &&3505 "internal op must be an atomic.update or atomic.write op");3506 3507 if (atomicWriteOp) {3508 isPostfixUpdate = true;3509 mlirExpr = atomicWriteOp.getExpr();3510 } else {3511 isPostfixUpdate = atomicCaptureOp.getSecondOp() ==3512 atomicCaptureOp.getAtomicUpdateOp().getOperation();3513 auto &innerOpList = atomicUpdateOp.getRegion().front().getOperations();3514 // Find the binary update operation that uses the region argument3515 // and get the expression to update3516 if (innerOpList.size() == 2) {3517 mlir::Operation &innerOp = *atomicUpdateOp.getRegion().front().begin();3518 if (!llvm::is_contained(innerOp.getOperands(),3519 atomicUpdateOp.getRegion().getArgument(0))) {3520 return atomicUpdateOp.emitError(3521 "no atomic update operation with region argument"3522 " as operand found inside atomic.update region");3523 }3524 binop = convertBinOpToAtomic(innerOp);3525 isXBinopExpr =3526 innerOp.getOperand(0) == atomicUpdateOp.getRegion().getArgument(0);3527 mlirExpr = (isXBinopExpr ? innerOp.getOperand(1) : innerOp.getOperand(0));3528 } else {3529 binop = llvm::AtomicRMWInst::BinOp::BAD_BINOP;3530 }3531 }3532 3533 llvm::Value *llvmExpr = moduleTranslation.lookupValue(mlirExpr);3534 llvm::Value *llvmX =3535 moduleTranslation.lookupValue(atomicCaptureOp.getAtomicReadOp().getX());3536 llvm::Value *llvmV =3537 moduleTranslation.lookupValue(atomicCaptureOp.getAtomicReadOp().getV());3538 llvm::Type *llvmXElementType = moduleTranslation.convertType(3539 atomicCaptureOp.getAtomicReadOp().getElementType());3540 llvm::OpenMPIRBuilder::AtomicOpValue llvmAtomicX = {llvmX, llvmXElementType,3541 /*isSigned=*/false,3542 /*isVolatile=*/false};3543 llvm::OpenMPIRBuilder::AtomicOpValue llvmAtomicV = {llvmV, llvmXElementType,3544 /*isSigned=*/false,3545 /*isVolatile=*/false};3546 3547 llvm::AtomicOrdering atomicOrdering =3548 convertAtomicOrdering(atomicCaptureOp.getMemoryOrder());3549 3550 auto updateFn =3551 [&](llvm::Value *atomicx,3552 llvm::IRBuilder<> &builder) -> llvm::Expected<llvm::Value *> {3553 if (atomicWriteOp)3554 return moduleTranslation.lookupValue(atomicWriteOp.getExpr());3555 Block &bb = *atomicUpdateOp.getRegion().begin();3556 moduleTranslation.mapValue(*atomicUpdateOp.getRegion().args_begin(),3557 atomicx);3558 moduleTranslation.mapBlock(&bb, builder.GetInsertBlock());3559 if (failed(moduleTranslation.convertBlock(bb, true, builder)))3560 return llvm::make_error<PreviouslyReportedError>();3561 3562 omp::YieldOp yieldop = dyn_cast<omp::YieldOp>(bb.getTerminator());3563 assert(yieldop && yieldop.getResults().size() == 1 &&3564 "terminator must be omp.yield op and it must have exactly one "3565 "argument");3566 return moduleTranslation.lookupValue(yieldop.getResults()[0]);3567 };3568 3569 bool isIgnoreDenormalMode;3570 bool isFineGrainedMemory;3571 bool isRemoteMemory;3572 extractAtomicControlFlags(atomicUpdateOp, isIgnoreDenormalMode,3573 isFineGrainedMemory, isRemoteMemory);3574 // Handle ambiguous alloca, if any.3575 auto allocaIP = findAllocaInsertPoint(builder, moduleTranslation);3576 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);3577 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =3578 ompBuilder->createAtomicCapture(3579 ompLoc, allocaIP, llvmAtomicX, llvmAtomicV, llvmExpr, atomicOrdering,3580 binop, updateFn, atomicUpdateOp, isPostfixUpdate, isXBinopExpr,3581 isIgnoreDenormalMode, isFineGrainedMemory, isRemoteMemory);3582 3583 if (failed(handleError(afterIP, *atomicCaptureOp)))3584 return failure();3585 3586 builder.restoreIP(*afterIP);3587 return success();3588}3589 3590static llvm::omp::Directive convertCancellationConstructType(3591 omp::ClauseCancellationConstructType directive) {3592 switch (directive) {3593 case omp::ClauseCancellationConstructType::Loop:3594 return llvm::omp::Directive::OMPD_for;3595 case omp::ClauseCancellationConstructType::Parallel:3596 return llvm::omp::Directive::OMPD_parallel;3597 case omp::ClauseCancellationConstructType::Sections:3598 return llvm::omp::Directive::OMPD_sections;3599 case omp::ClauseCancellationConstructType::Taskgroup:3600 return llvm::omp::Directive::OMPD_taskgroup;3601 }3602 llvm_unreachable("Unhandled cancellation construct type");3603}3604 3605static LogicalResult3606convertOmpCancel(omp::CancelOp op, llvm::IRBuilderBase &builder,3607 LLVM::ModuleTranslation &moduleTranslation) {3608 if (failed(checkImplementationStatus(*op.getOperation())))3609 return failure();3610 3611 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);3612 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3613 3614 llvm::Value *ifCond = nullptr;3615 if (Value ifVar = op.getIfExpr())3616 ifCond = moduleTranslation.lookupValue(ifVar);3617 3618 llvm::omp::Directive cancelledDirective =3619 convertCancellationConstructType(op.getCancelDirective());3620 3621 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =3622 ompBuilder->createCancel(ompLoc, ifCond, cancelledDirective);3623 3624 if (failed(handleError(afterIP, *op.getOperation())))3625 return failure();3626 3627 builder.restoreIP(afterIP.get());3628 3629 return success();3630}3631 3632static LogicalResult3633convertOmpCancellationPoint(omp::CancellationPointOp op,3634 llvm::IRBuilderBase &builder,3635 LLVM::ModuleTranslation &moduleTranslation) {3636 if (failed(checkImplementationStatus(*op.getOperation())))3637 return failure();3638 3639 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);3640 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3641 3642 llvm::omp::Directive cancelledDirective =3643 convertCancellationConstructType(op.getCancelDirective());3644 3645 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =3646 ompBuilder->createCancellationPoint(ompLoc, cancelledDirective);3647 3648 if (failed(handleError(afterIP, *op.getOperation())))3649 return failure();3650 3651 builder.restoreIP(afterIP.get());3652 3653 return success();3654}3655 3656/// Converts an OpenMP Threadprivate operation into LLVM IR using3657/// OpenMPIRBuilder.3658static LogicalResult3659convertOmpThreadprivate(Operation &opInst, llvm::IRBuilderBase &builder,3660 LLVM::ModuleTranslation &moduleTranslation) {3661 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);3662 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3663 auto threadprivateOp = cast<omp::ThreadprivateOp>(opInst);3664 3665 if (failed(checkImplementationStatus(opInst)))3666 return failure();3667 3668 Value symAddr = threadprivateOp.getSymAddr();3669 auto *symOp = symAddr.getDefiningOp();3670 3671 if (auto asCast = dyn_cast<LLVM::AddrSpaceCastOp>(symOp))3672 symOp = asCast.getOperand().getDefiningOp();3673 3674 if (!isa<LLVM::AddressOfOp>(symOp))3675 return opInst.emitError("Addressing symbol not found");3676 LLVM::AddressOfOp addressOfOp = dyn_cast<LLVM::AddressOfOp>(symOp);3677 3678 LLVM::GlobalOp global =3679 addressOfOp.getGlobal(moduleTranslation.symbolTable());3680 llvm::GlobalValue *globalValue = moduleTranslation.lookupGlobal(global);3681 3682 if (!ompBuilder->Config.isTargetDevice()) {3683 llvm::Type *type = globalValue->getValueType();3684 llvm::TypeSize typeSize =3685 builder.GetInsertBlock()->getModule()->getDataLayout().getTypeStoreSize(3686 type);3687 llvm::ConstantInt *size = builder.getInt64(typeSize.getFixedValue());3688 llvm::Value *callInst = ompBuilder->createCachedThreadPrivate(3689 ompLoc, globalValue, size, global.getSymName() + ".cache");3690 moduleTranslation.mapValue(opInst.getResult(0), callInst);3691 } else {3692 moduleTranslation.mapValue(opInst.getResult(0), globalValue);3693 }3694 3695 return success();3696}3697 3698static llvm::OffloadEntriesInfoManager::OMPTargetDeviceClauseKind3699convertToDeviceClauseKind(mlir::omp::DeclareTargetDeviceType deviceClause) {3700 switch (deviceClause) {3701 case mlir::omp::DeclareTargetDeviceType::host:3702 return llvm::OffloadEntriesInfoManager::OMPTargetDeviceClauseHost;3703 break;3704 case mlir::omp::DeclareTargetDeviceType::nohost:3705 return llvm::OffloadEntriesInfoManager::OMPTargetDeviceClauseNoHost;3706 break;3707 case mlir::omp::DeclareTargetDeviceType::any:3708 return llvm::OffloadEntriesInfoManager::OMPTargetDeviceClauseAny;3709 break;3710 }3711 llvm_unreachable("unhandled device clause");3712}3713 3714static llvm::OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind3715convertToCaptureClauseKind(3716 mlir::omp::DeclareTargetCaptureClause captureClause) {3717 switch (captureClause) {3718 case mlir::omp::DeclareTargetCaptureClause::to:3719 return llvm::OffloadEntriesInfoManager::OMPTargetGlobalVarEntryTo;3720 case mlir::omp::DeclareTargetCaptureClause::link:3721 return llvm::OffloadEntriesInfoManager::OMPTargetGlobalVarEntryLink;3722 case mlir::omp::DeclareTargetCaptureClause::enter:3723 return llvm::OffloadEntriesInfoManager::OMPTargetGlobalVarEntryEnter;3724 case mlir::omp::DeclareTargetCaptureClause::none:3725 return llvm::OffloadEntriesInfoManager::OMPTargetGlobalVarEntryNone;3726 }3727 llvm_unreachable("unhandled capture clause");3728}3729 3730static Operation *getGlobalOpFromValue(Value value) {3731 Operation *op = value.getDefiningOp();3732 if (auto addrCast = dyn_cast_if_present<LLVM::AddrSpaceCastOp>(op))3733 op = addrCast->getOperand(0).getDefiningOp();3734 if (auto addressOfOp = dyn_cast_if_present<LLVM::AddressOfOp>(op)) {3735 auto modOp = addressOfOp->getParentOfType<mlir::ModuleOp>();3736 return modOp.lookupSymbol(addressOfOp.getGlobalName());3737 }3738 return nullptr;3739}3740 3741static llvm::SmallString<64>3742getDeclareTargetRefPtrSuffix(LLVM::GlobalOp globalOp,3743 llvm::OpenMPIRBuilder &ompBuilder) {3744 llvm::SmallString<64> suffix;3745 llvm::raw_svector_ostream os(suffix);3746 if (globalOp.getVisibility() == mlir::SymbolTable::Visibility::Private) {3747 auto loc = globalOp->getLoc()->findInstanceOf<FileLineColLoc>();3748 auto fileInfoCallBack = [&loc]() {3749 return std::pair<std::string, uint64_t>(3750 llvm::StringRef(loc.getFilename()), loc.getLine());3751 };3752 3753 auto vfs = llvm::vfs::getRealFileSystem();3754 os << llvm::format(3755 "_%x",3756 ompBuilder.getTargetEntryUniqueInfo(fileInfoCallBack, *vfs).FileID);3757 }3758 os << "_decl_tgt_ref_ptr";3759 3760 return suffix;3761}3762 3763static bool isDeclareTargetLink(Value value) {3764 if (auto declareTargetGlobal =3765 dyn_cast_if_present<omp::DeclareTargetInterface>(3766 getGlobalOpFromValue(value)))3767 if (declareTargetGlobal.getDeclareTargetCaptureClause() ==3768 omp::DeclareTargetCaptureClause::link)3769 return true;3770 return false;3771}3772 3773static bool isDeclareTargetTo(Value value) {3774 if (auto declareTargetGlobal =3775 dyn_cast_if_present<omp::DeclareTargetInterface>(3776 getGlobalOpFromValue(value)))3777 if (declareTargetGlobal.getDeclareTargetCaptureClause() ==3778 omp::DeclareTargetCaptureClause::to ||3779 declareTargetGlobal.getDeclareTargetCaptureClause() ==3780 omp::DeclareTargetCaptureClause::enter)3781 return true;3782 return false;3783}3784 3785// Returns the reference pointer generated by the lowering of the declare3786// target operation in cases where the link clause is used or the to clause is3787// used in USM mode.3788static llvm::Value *3789getRefPtrIfDeclareTarget(Value value,3790 LLVM::ModuleTranslation &moduleTranslation) {3791 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();3792 if (auto gOp =3793 dyn_cast_or_null<LLVM::GlobalOp>(getGlobalOpFromValue(value))) {3794 if (auto declareTargetGlobal =3795 dyn_cast<omp::DeclareTargetInterface>(gOp.getOperation())) {3796 // In this case, we must utilise the reference pointer generated by3797 // the declare target operation, similar to Clang3798 if ((declareTargetGlobal.getDeclareTargetCaptureClause() ==3799 omp::DeclareTargetCaptureClause::link) ||3800 (declareTargetGlobal.getDeclareTargetCaptureClause() ==3801 omp::DeclareTargetCaptureClause::to &&3802 ompBuilder->Config.hasRequiresUnifiedSharedMemory())) {3803 llvm::SmallString<64> suffix =3804 getDeclareTargetRefPtrSuffix(gOp, *ompBuilder);3805 3806 if (gOp.getSymName().contains(suffix))3807 return moduleTranslation.getLLVMModule()->getNamedValue(3808 gOp.getSymName());3809 3810 return moduleTranslation.getLLVMModule()->getNamedValue(3811 (gOp.getSymName().str() + suffix.str()).str());3812 }3813 }3814 }3815 return nullptr;3816}3817 3818namespace {3819// Append customMappers information to existing MapInfosTy3820struct MapInfosTy : llvm::OpenMPIRBuilder::MapInfosTy {3821 SmallVector<Operation *, 4> Mappers;3822 3823 /// Append arrays in \a CurInfo.3824 void append(MapInfosTy &curInfo) {3825 Mappers.append(curInfo.Mappers.begin(), curInfo.Mappers.end());3826 llvm::OpenMPIRBuilder::MapInfosTy::append(curInfo);3827 }3828};3829// A small helper structure to contain data gathered3830// for map lowering and coalese it into one area and3831// avoiding extra computations such as searches in the3832// llvm module for lowered mapped variables or checking3833// if something is declare target (and retrieving the3834// value) more than neccessary.3835struct MapInfoData : MapInfosTy {3836 llvm::SmallVector<bool, 4> IsDeclareTarget;3837 llvm::SmallVector<bool, 4> IsAMember;3838 // Identify if mapping was added by mapClause or use_device clauses.3839 llvm::SmallVector<bool, 4> IsAMapping;3840 llvm::SmallVector<mlir::Operation *, 4> MapClause;3841 llvm::SmallVector<llvm::Value *, 4> OriginalValue;3842 // Stripped off array/pointer to get the underlying3843 // element type3844 llvm::SmallVector<llvm::Type *, 4> BaseType;3845 3846 /// Append arrays in \a CurInfo.3847 void append(MapInfoData &CurInfo) {3848 IsDeclareTarget.append(CurInfo.IsDeclareTarget.begin(),3849 CurInfo.IsDeclareTarget.end());3850 MapClause.append(CurInfo.MapClause.begin(), CurInfo.MapClause.end());3851 OriginalValue.append(CurInfo.OriginalValue.begin(),3852 CurInfo.OriginalValue.end());3853 BaseType.append(CurInfo.BaseType.begin(), CurInfo.BaseType.end());3854 MapInfosTy::append(CurInfo);3855 }3856};3857 3858enum class TargetDirectiveEnumTy : uint32_t {3859 None = 0,3860 Target = 1,3861 TargetData = 2,3862 TargetEnterData = 3,3863 TargetExitData = 4,3864 TargetUpdate = 53865};3866 3867static TargetDirectiveEnumTy getTargetDirectiveEnumTyFromOp(Operation *op) {3868 return llvm::TypeSwitch<Operation *, TargetDirectiveEnumTy>(op)3869 .Case([](omp::TargetDataOp) { return TargetDirectiveEnumTy::TargetData; })3870 .Case([](omp::TargetEnterDataOp) {3871 return TargetDirectiveEnumTy::TargetEnterData;3872 })3873 .Case([&](omp::TargetExitDataOp) {3874 return TargetDirectiveEnumTy::TargetExitData;3875 })3876 .Case([&](omp::TargetUpdateOp) {3877 return TargetDirectiveEnumTy::TargetUpdate;3878 })3879 .Case([&](omp::TargetOp) { return TargetDirectiveEnumTy::Target; })3880 .Default([&](Operation *op) { return TargetDirectiveEnumTy::None; });3881}3882 3883} // namespace3884 3885static uint64_t getArrayElementSizeInBits(LLVM::LLVMArrayType arrTy,3886 DataLayout &dl) {3887 if (auto nestedArrTy = llvm::dyn_cast_if_present<LLVM::LLVMArrayType>(3888 arrTy.getElementType()))3889 return getArrayElementSizeInBits(nestedArrTy, dl);3890 return dl.getTypeSizeInBits(arrTy.getElementType());3891}3892 3893// This function calculates the size to be offloaded for a specified type, given3894// its associated map clause (which can contain bounds information which affects3895// the total size), this size is calculated based on the underlying element type3896// e.g. given a 1-D array of ints, we will calculate the size from the integer3897// type * number of elements in the array. This size can be used in other3898// calculations but is ultimately used as an argument to the OpenMP runtimes3899// kernel argument structure which is generated through the combinedInfo data3900// structures.3901// This function is somewhat equivalent to Clang's getExprTypeSize inside of3902// CGOpenMPRuntime.cpp.3903static llvm::Value *getSizeInBytes(DataLayout &dl, const mlir::Type &type,3904 Operation *clauseOp,3905 llvm::Value *basePointer,3906 llvm::Type *baseType,3907 llvm::IRBuilderBase &builder,3908 LLVM::ModuleTranslation &moduleTranslation) {3909 if (auto memberClause =3910 mlir::dyn_cast_if_present<mlir::omp::MapInfoOp>(clauseOp)) {3911 // This calculates the size to transfer based on bounds and the underlying3912 // element type, provided bounds have been specified (Fortran3913 // pointers/allocatables/target and arrays that have sections specified fall3914 // into this as well)3915 if (!memberClause.getBounds().empty()) {3916 llvm::Value *elementCount = builder.getInt64(1);3917 for (auto bounds : memberClause.getBounds()) {3918 if (auto boundOp = mlir::dyn_cast_if_present<mlir::omp::MapBoundsOp>(3919 bounds.getDefiningOp())) {3920 // The below calculation for the size to be mapped calculated from the3921 // map.info's bounds is: (elemCount * [UB - LB] + 1), later we3922 // multiply by the underlying element types byte size to get the full3923 // size to be offloaded based on the bounds3924 elementCount = builder.CreateMul(3925 elementCount,3926 builder.CreateAdd(3927 builder.CreateSub(3928 moduleTranslation.lookupValue(boundOp.getUpperBound()),3929 moduleTranslation.lookupValue(boundOp.getLowerBound())),3930 builder.getInt64(1)));3931 }3932 }3933 3934 // utilising getTypeSizeInBits instead of getTypeSize as getTypeSize gives3935 // the size in inconsistent byte or bit format.3936 uint64_t underlyingTypeSzInBits = dl.getTypeSizeInBits(type);3937 if (auto arrTy = llvm::dyn_cast_if_present<LLVM::LLVMArrayType>(type))3938 underlyingTypeSzInBits = getArrayElementSizeInBits(arrTy, dl);3939 3940 // The size in bytes x number of elements, the sizeInBytes stored is3941 // the underyling types size, e.g. if ptr<i32>, it'll be the i32's3942 // size, so we do some on the fly runtime math to get the size in3943 // bytes from the extent (ub - lb) * sizeInBytes. NOTE: This may need3944 // some adjustment for members with more complex types.3945 return builder.CreateMul(elementCount,3946 builder.getInt64(underlyingTypeSzInBits / 8));3947 }3948 }3949 3950 return builder.getInt64(dl.getTypeSizeInBits(type) / 8);3951}3952 3953// Convert the MLIR map flag set to the runtime map flag set for embedding3954// in LLVM-IR. This is important as the two bit-flag lists do not correspond3955// 1-to-1 as there's flags the runtime doesn't care about and vice versa.3956// Certain flags are discarded here such as RefPtee and co.3957static llvm::omp::OpenMPOffloadMappingFlags3958convertClauseMapFlags(omp::ClauseMapFlags mlirFlags) {3959 auto mapTypeToBool = [&mlirFlags](omp::ClauseMapFlags flag) {3960 return (mlirFlags & flag) == flag;3961 };3962 3963 llvm::omp::OpenMPOffloadMappingFlags mapType =3964 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE;3965 3966 if (mapTypeToBool(omp::ClauseMapFlags::to))3967 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO;3968 3969 if (mapTypeToBool(omp::ClauseMapFlags::from))3970 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;3971 3972 if (mapTypeToBool(omp::ClauseMapFlags::always))3973 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS;3974 3975 if (mapTypeToBool(omp::ClauseMapFlags::del))3976 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_DELETE;3977 3978 if (mapTypeToBool(omp::ClauseMapFlags::return_param))3979 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_RETURN_PARAM;3980 3981 if (mapTypeToBool(omp::ClauseMapFlags::priv))3982 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_PRIVATE;3983 3984 if (mapTypeToBool(omp::ClauseMapFlags::literal))3985 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_LITERAL;3986 3987 if (mapTypeToBool(omp::ClauseMapFlags::implicit))3988 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT;3989 3990 if (mapTypeToBool(omp::ClauseMapFlags::close))3991 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_CLOSE;3992 3993 if (mapTypeToBool(omp::ClauseMapFlags::present))3994 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_PRESENT;3995 3996 if (mapTypeToBool(omp::ClauseMapFlags::ompx_hold))3997 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_OMPX_HOLD;3998 3999 if (mapTypeToBool(omp::ClauseMapFlags::attach))4000 mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ATTACH;4001 4002 return mapType;4003}4004 4005static void collectMapDataFromMapOperands(4006 MapInfoData &mapData, SmallVectorImpl<Value> &mapVars,4007 LLVM::ModuleTranslation &moduleTranslation, DataLayout &dl,4008 llvm::IRBuilderBase &builder, ArrayRef<Value> useDevPtrOperands = {},4009 ArrayRef<Value> useDevAddrOperands = {},4010 ArrayRef<Value> hasDevAddrOperands = {}) {4011 auto checkIsAMember = [](const auto &mapVars, auto mapOp) {4012 // Check if this is a member mapping and correctly assign that it is, if4013 // it is a member of a larger object.4014 // TODO: Need better handling of members, and distinguishing of members4015 // that are implicitly allocated on device vs explicitly passed in as4016 // arguments.4017 // TODO: May require some further additions to support nested record4018 // types, i.e. member maps that can have member maps.4019 for (Value mapValue : mapVars) {4020 auto map = cast<omp::MapInfoOp>(mapValue.getDefiningOp());4021 for (auto member : map.getMembers())4022 if (member == mapOp)4023 return true;4024 }4025 return false;4026 };4027 4028 // Process MapOperands4029 for (Value mapValue : mapVars) {4030 auto mapOp = cast<omp::MapInfoOp>(mapValue.getDefiningOp());4031 Value offloadPtr =4032 mapOp.getVarPtrPtr() ? mapOp.getVarPtrPtr() : mapOp.getVarPtr();4033 mapData.OriginalValue.push_back(moduleTranslation.lookupValue(offloadPtr));4034 mapData.Pointers.push_back(mapData.OriginalValue.back());4035 4036 if (llvm::Value *refPtr =4037 getRefPtrIfDeclareTarget(offloadPtr, moduleTranslation)) {4038 mapData.IsDeclareTarget.push_back(true);4039 mapData.BasePointers.push_back(refPtr);4040 } else if (isDeclareTargetTo(offloadPtr)) {4041 mapData.IsDeclareTarget.push_back(true);4042 mapData.BasePointers.push_back(mapData.OriginalValue.back());4043 } else { // regular mapped variable4044 mapData.IsDeclareTarget.push_back(false);4045 mapData.BasePointers.push_back(mapData.OriginalValue.back());4046 }4047 4048 mapData.BaseType.push_back(4049 moduleTranslation.convertType(mapOp.getVarType()));4050 mapData.Sizes.push_back(4051 getSizeInBytes(dl, mapOp.getVarType(), mapOp, mapData.Pointers.back(),4052 mapData.BaseType.back(), builder, moduleTranslation));4053 mapData.MapClause.push_back(mapOp.getOperation());4054 mapData.Types.push_back(convertClauseMapFlags(mapOp.getMapType()));4055 mapData.Names.push_back(LLVM::createMappingInformation(4056 mapOp.getLoc(), *moduleTranslation.getOpenMPBuilder()));4057 mapData.DevicePointers.push_back(llvm::OpenMPIRBuilder::DeviceInfoTy::None);4058 if (mapOp.getMapperId())4059 mapData.Mappers.push_back(4060 SymbolTable::lookupNearestSymbolFrom<omp::DeclareMapperOp>(4061 mapOp, mapOp.getMapperIdAttr()));4062 else4063 mapData.Mappers.push_back(nullptr);4064 mapData.IsAMapping.push_back(true);4065 mapData.IsAMember.push_back(checkIsAMember(mapVars, mapOp));4066 }4067 4068 auto findMapInfo = [&mapData](llvm::Value *val,4069 llvm::OpenMPIRBuilder::DeviceInfoTy devInfoTy) {4070 unsigned index = 0;4071 bool found = false;4072 for (llvm::Value *basePtr : mapData.OriginalValue) {4073 if (basePtr == val && mapData.IsAMapping[index]) {4074 found = true;4075 mapData.Types[index] |=4076 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_RETURN_PARAM;4077 mapData.DevicePointers[index] = devInfoTy;4078 }4079 index++;4080 }4081 return found;4082 };4083 4084 // Process useDevPtr(Addr)Operands4085 auto addDevInfos = [&](const llvm::ArrayRef<Value> &useDevOperands,4086 llvm::OpenMPIRBuilder::DeviceInfoTy devInfoTy) {4087 for (Value mapValue : useDevOperands) {4088 auto mapOp = cast<omp::MapInfoOp>(mapValue.getDefiningOp());4089 Value offloadPtr =4090 mapOp.getVarPtrPtr() ? mapOp.getVarPtrPtr() : mapOp.getVarPtr();4091 llvm::Value *origValue = moduleTranslation.lookupValue(offloadPtr);4092 4093 // Check if map info is already present for this entry.4094 if (!findMapInfo(origValue, devInfoTy)) {4095 mapData.OriginalValue.push_back(origValue);4096 mapData.Pointers.push_back(mapData.OriginalValue.back());4097 mapData.IsDeclareTarget.push_back(false);4098 mapData.BasePointers.push_back(mapData.OriginalValue.back());4099 mapData.BaseType.push_back(4100 moduleTranslation.convertType(mapOp.getVarType()));4101 mapData.Sizes.push_back(builder.getInt64(0));4102 mapData.MapClause.push_back(mapOp.getOperation());4103 mapData.Types.push_back(4104 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_RETURN_PARAM);4105 mapData.Names.push_back(LLVM::createMappingInformation(4106 mapOp.getLoc(), *moduleTranslation.getOpenMPBuilder()));4107 mapData.DevicePointers.push_back(devInfoTy);4108 mapData.Mappers.push_back(nullptr);4109 mapData.IsAMapping.push_back(false);4110 mapData.IsAMember.push_back(checkIsAMember(useDevOperands, mapOp));4111 }4112 }4113 };4114 4115 addDevInfos(useDevAddrOperands, llvm::OpenMPIRBuilder::DeviceInfoTy::Address);4116 addDevInfos(useDevPtrOperands, llvm::OpenMPIRBuilder::DeviceInfoTy::Pointer);4117 4118 for (Value mapValue : hasDevAddrOperands) {4119 auto mapOp = cast<omp::MapInfoOp>(mapValue.getDefiningOp());4120 Value offloadPtr =4121 mapOp.getVarPtrPtr() ? mapOp.getVarPtrPtr() : mapOp.getVarPtr();4122 llvm::Value *origValue = moduleTranslation.lookupValue(offloadPtr);4123 auto mapType = convertClauseMapFlags(mapOp.getMapType());4124 auto mapTypeAlways = llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS;4125 4126 mapData.OriginalValue.push_back(origValue);4127 mapData.BasePointers.push_back(origValue);4128 mapData.Pointers.push_back(origValue);4129 mapData.IsDeclareTarget.push_back(false);4130 mapData.BaseType.push_back(4131 moduleTranslation.convertType(mapOp.getVarType()));4132 mapData.Sizes.push_back(4133 builder.getInt64(dl.getTypeSize(mapOp.getVarType())));4134 mapData.MapClause.push_back(mapOp.getOperation());4135 if (llvm::to_underlying(mapType & mapTypeAlways)) {4136 // Descriptors are mapped with the ALWAYS flag, since they can get4137 // rematerialized, so the address of the decriptor for a given object4138 // may change from one place to another.4139 mapData.Types.push_back(mapType);4140 // Technically it's possible for a non-descriptor mapping to have4141 // both has-device-addr and ALWAYS, so lookup the mapper in case it4142 // exists.4143 if (mapOp.getMapperId()) {4144 mapData.Mappers.push_back(4145 SymbolTable::lookupNearestSymbolFrom<omp::DeclareMapperOp>(4146 mapOp, mapOp.getMapperIdAttr()));4147 } else {4148 mapData.Mappers.push_back(nullptr);4149 }4150 } else {4151 mapData.Types.push_back(4152 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_LITERAL);4153 mapData.Mappers.push_back(nullptr);4154 }4155 mapData.Names.push_back(LLVM::createMappingInformation(4156 mapOp.getLoc(), *moduleTranslation.getOpenMPBuilder()));4157 mapData.DevicePointers.push_back(4158 llvm::OpenMPIRBuilder::DeviceInfoTy::Address);4159 mapData.IsAMapping.push_back(false);4160 mapData.IsAMember.push_back(checkIsAMember(hasDevAddrOperands, mapOp));4161 }4162}4163 4164static int getMapDataMemberIdx(MapInfoData &mapData, omp::MapInfoOp memberOp) {4165 auto *res = llvm::find(mapData.MapClause, memberOp);4166 assert(res != mapData.MapClause.end() &&4167 "MapInfoOp for member not found in MapData, cannot return index");4168 return std::distance(mapData.MapClause.begin(), res);4169}4170 4171static void sortMapIndices(llvm::SmallVectorImpl<size_t> &indices,4172 omp::MapInfoOp mapInfo) {4173 ArrayAttr indexAttr = mapInfo.getMembersIndexAttr();4174 llvm::SmallVector<size_t> occludedChildren;4175 llvm::sort(4176 indices.begin(), indices.end(), [&](const size_t a, const size_t b) {4177 // Bail early if we are asked to look at the same index. If we do not4178 // bail early, we can end up mistakenly adding indices to4179 // occludedChildren. This can occur with some types of libc++ hardening.4180 if (a == b)4181 return false;4182 4183 auto memberIndicesA = cast<ArrayAttr>(indexAttr[a]);4184 auto memberIndicesB = cast<ArrayAttr>(indexAttr[b]);4185 4186 for (auto it : llvm::zip(memberIndicesA, memberIndicesB)) {4187 int64_t aIndex = mlir::cast<IntegerAttr>(std::get<0>(it)).getInt();4188 int64_t bIndex = mlir::cast<IntegerAttr>(std::get<1>(it)).getInt();4189 4190 if (aIndex == bIndex)4191 continue;4192 4193 if (aIndex < bIndex)4194 return true;4195 4196 if (aIndex > bIndex)4197 return false;4198 }4199 4200 // Iterated up until the end of the smallest member and4201 // they were found to be equal up to that point, so select4202 // the member with the lowest index count, so the "parent"4203 bool memberAParent = memberIndicesA.size() < memberIndicesB.size();4204 if (memberAParent)4205 occludedChildren.push_back(b);4206 else4207 occludedChildren.push_back(a);4208 return memberAParent;4209 });4210 4211 // We remove children from the index list that are overshadowed by4212 // a parent, this prevents us retrieving these as the first or last4213 // element when the parent is the correct element in these cases.4214 for (auto v : occludedChildren)4215 indices.erase(std::remove(indices.begin(), indices.end(), v),4216 indices.end());4217}4218 4219static omp::MapInfoOp getFirstOrLastMappedMemberPtr(omp::MapInfoOp mapInfo,4220 bool first) {4221 ArrayAttr indexAttr = mapInfo.getMembersIndexAttr();4222 // Only 1 member has been mapped, we can return it.4223 if (indexAttr.size() == 1)4224 return cast<omp::MapInfoOp>(mapInfo.getMembers()[0].getDefiningOp());4225 llvm::SmallVector<size_t> indices(indexAttr.size());4226 std::iota(indices.begin(), indices.end(), 0);4227 sortMapIndices(indices, mapInfo);4228 return llvm::cast<omp::MapInfoOp>(4229 mapInfo.getMembers()[first ? indices.front() : indices.back()]4230 .getDefiningOp());4231}4232 4233/// This function calculates the array/pointer offset for map data provided4234/// with bounds operations, e.g. when provided something like the following:4235///4236/// Fortran4237/// map(tofrom: array(2:5, 3:2))4238///4239/// We must calculate the initial pointer offset to pass across, this function4240/// performs this using bounds.4241///4242/// TODO/WARNING: This only supports Fortran's column major indexing currently4243/// as is noted in the note below and comments in the function, we must extend4244/// this function when we add a C++ frontend.4245/// NOTE: which while specified in row-major order it currently needs to be4246/// flipped for Fortran's column order array allocation and access (as4247/// opposed to C++'s row-major, hence the backwards processing where order is4248/// important). This is likely important to keep in mind for the future when4249/// we incorporate a C++ frontend, both frontends will need to agree on the4250/// ordering of generated bounds operations (one may have to flip them) to4251/// make the below lowering frontend agnostic. The offload size4252/// calcualtion may also have to be adjusted for C++.4253static std::vector<llvm::Value *>4254calculateBoundsOffset(LLVM::ModuleTranslation &moduleTranslation,4255 llvm::IRBuilderBase &builder, bool isArrayTy,4256 OperandRange bounds) {4257 std::vector<llvm::Value *> idx;4258 // There's no bounds to calculate an offset from, we can safely4259 // ignore and return no indices.4260 if (bounds.empty())4261 return idx;4262 4263 // If we have an array type, then we have its type so can treat it as a4264 // normal GEP instruction where the bounds operations are simply indexes4265 // into the array. We currently do reverse order of the bounds, which4266 // I believe leans more towards Fortran's column-major in memory.4267 if (isArrayTy) {4268 idx.push_back(builder.getInt64(0));4269 for (int i = bounds.size() - 1; i >= 0; --i) {4270 if (auto boundOp = dyn_cast_if_present<omp::MapBoundsOp>(4271 bounds[i].getDefiningOp())) {4272 idx.push_back(moduleTranslation.lookupValue(boundOp.getLowerBound()));4273 }4274 }4275 } else {4276 // If we do not have an array type, but we have bounds, then we're dealing4277 // with a pointer that's being treated like an array and we have the4278 // underlying type e.g. an i32, or f64 etc, e.g. a fortran descriptor base4279 // address (pointer pointing to the actual data) so we must caclulate the4280 // offset using a single index which the following loop attempts to4281 // compute using the standard column-major algorithm e.g for a 3D array:4282 //4283 // ((((c_idx * b_len) + b_idx) * a_len) + a_idx)4284 //4285 // It is of note that it's doing column-major rather than row-major at the4286 // moment, but having a way for the frontend to indicate which major format4287 // to use or standardizing/canonicalizing the order of the bounds to compute4288 // the offset may be useful in the future when there's other frontends with4289 // different formats.4290 std::vector<llvm::Value *> dimensionIndexSizeOffset;4291 for (int i = bounds.size() - 1; i >= 0; --i) {4292 if (auto boundOp = dyn_cast_if_present<omp::MapBoundsOp>(4293 bounds[i].getDefiningOp())) {4294 if (i == ((int)bounds.size() - 1))4295 idx.emplace_back(4296 moduleTranslation.lookupValue(boundOp.getLowerBound()));4297 else4298 idx.back() = builder.CreateAdd(4299 builder.CreateMul(idx.back(), moduleTranslation.lookupValue(4300 boundOp.getExtent())),4301 moduleTranslation.lookupValue(boundOp.getLowerBound()));4302 }4303 }4304 }4305 4306 return idx;4307}4308 4309static void getAsIntegers(ArrayAttr values, llvm::SmallVector<int64_t> &ints) {4310 llvm::transform(values, std::back_inserter(ints), [](Attribute value) {4311 return cast<IntegerAttr>(value).getInt();4312 });4313}4314 4315// Gathers members that are overlapping in the parent, excluding members that4316// themselves overlap, keeping the top-most (closest to parents level) map.4317static void4318getOverlappedMembers(llvm::SmallVectorImpl<size_t> &overlapMapDataIdxs,4319 omp::MapInfoOp parentOp) {4320 // No members mapped, no overlaps.4321 if (parentOp.getMembers().empty())4322 return;4323 4324 // Single member, we can insert and return early.4325 if (parentOp.getMembers().size() == 1) {4326 overlapMapDataIdxs.push_back(0);4327 return;4328 }4329 4330 // 1) collect list of top-level overlapping members from MemberOp4331 llvm::SmallVector<std::pair<int, ArrayAttr>> memberByIndex;4332 ArrayAttr indexAttr = parentOp.getMembersIndexAttr();4333 for (auto [memIndex, indicesAttr] : llvm::enumerate(indexAttr))4334 memberByIndex.push_back(4335 std::make_pair(memIndex, cast<ArrayAttr>(indicesAttr)));4336 4337 // Sort the smallest first (higher up the parent -> member chain), so that4338 // when we remove members, we remove as much as we can in the initial4339 // iterations, shortening the number of passes required.4340 llvm::sort(memberByIndex.begin(), memberByIndex.end(),4341 [&](auto a, auto b) { return a.second.size() < b.second.size(); });4342 4343 // Remove elements from the vector if there is a parent element that4344 // supersedes it. i.e. if member [0] is mapped, we can remove members [0,1],4345 // [0,2].. etc.4346 llvm::SmallVector<std::pair<int, ArrayAttr>> skipList;4347 for (auto v : memberByIndex) {4348 llvm::SmallVector<int64_t> vArr(v.second.size());4349 getAsIntegers(v.second, vArr);4350 skipList.push_back(4351 *std::find_if(memberByIndex.begin(), memberByIndex.end(), [&](auto x) {4352 if (v == x)4353 return false;4354 llvm::SmallVector<int64_t> xArr(x.second.size());4355 getAsIntegers(x.second, xArr);4356 return std::equal(vArr.begin(), vArr.end(), xArr.begin()) &&4357 xArr.size() >= vArr.size();4358 }));4359 }4360 4361 // Collect the indices, as we need the base pointer etc. from the MapData4362 // structure which is primarily accessible via index at the moment.4363 for (auto v : memberByIndex)4364 if (find(skipList.begin(), skipList.end(), v) == skipList.end())4365 overlapMapDataIdxs.push_back(v.first);4366}4367 4368// The intent is to verify if the mapped data being passed is a4369// pointer -> pointee that requires special handling in certain cases,4370// e.g. applying the OMP_MAP_PTR_AND_OBJ map type.4371//4372// There may be a better way to verify this, but unfortunately with4373// opaque pointers we lose the ability to easily check if something is4374// a pointer whilst maintaining access to the underlying type.4375static bool checkIfPointerMap(omp::MapInfoOp mapOp) {4376 // If we have a varPtrPtr field assigned then the underlying type is a pointer4377 if (mapOp.getVarPtrPtr())4378 return true;4379 4380 // If the map data is declare target with a link clause, then it's represented4381 // as a pointer when we lower it to LLVM-IR even if at the MLIR level it has4382 // no relation to pointers.4383 if (isDeclareTargetLink(mapOp.getVarPtr()))4384 return true;4385 4386 return false;4387}4388 4389// This creates two insertions into the MapInfosTy data structure for the4390// "parent" of a set of members, (usually a container e.g.4391// class/structure/derived type) when subsequent members have also been4392// explicitly mapped on the same map clause. Certain types, such as Fortran4393// descriptors are mapped like this as well, however, the members are4394// implicit as far as a user is concerned, but we must explicitly map them4395// internally.4396//4397// This function also returns the memberOfFlag for this particular parent,4398// which is utilised in subsequent member mappings (by modifying there map type4399// with it) to indicate that a member is part of this parent and should be4400// treated by the runtime as such. Important to achieve the correct mapping.4401//4402// This function borrows a lot from Clang's emitCombinedEntry function4403// inside of CGOpenMPRuntime.cpp4404static llvm::omp::OpenMPOffloadMappingFlags mapParentWithMembers(4405 LLVM::ModuleTranslation &moduleTranslation, llvm::IRBuilderBase &builder,4406 llvm::OpenMPIRBuilder &ompBuilder, DataLayout &dl, MapInfosTy &combinedInfo,4407 MapInfoData &mapData, uint64_t mapDataIndex,4408 TargetDirectiveEnumTy targetDirective) {4409 assert(!ompBuilder.Config.isTargetDevice() &&4410 "function only supported for host device codegen");4411 4412 // Map the first segment of the parent. If a user-defined mapper is attached,4413 // include the parent's to/from-style bits (and common modifiers) in this4414 // base entry so the mapper receives correct copy semantics via its 'type'4415 // parameter. Also keep TARGET_PARAM when required for kernel arguments.4416 llvm::omp::OpenMPOffloadMappingFlags baseFlag =4417 (targetDirective == TargetDirectiveEnumTy::Target &&4418 !mapData.IsDeclareTarget[mapDataIndex])4419 ? llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TARGET_PARAM4420 : llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE;4421 4422 // Detect if this mapping uses a user-defined mapper.4423 bool hasUserMapper = mapData.Mappers[mapDataIndex] != nullptr;4424 if (hasUserMapper) {4425 using mapFlags = llvm::omp::OpenMPOffloadMappingFlags;4426 // Preserve relevant map-type bits from the parent clause. These include4427 // the copy direction (TO/FROM), as well as commonly used modifiers that4428 // should be visible to the mapper for correct behaviour.4429 mapFlags parentFlags = mapData.Types[mapDataIndex];4430 mapFlags preserve = mapFlags::OMP_MAP_TO | mapFlags::OMP_MAP_FROM |4431 mapFlags::OMP_MAP_ALWAYS | mapFlags::OMP_MAP_CLOSE |4432 mapFlags::OMP_MAP_PRESENT | mapFlags::OMP_MAP_OMPX_HOLD;4433 baseFlag |= (parentFlags & preserve);4434 }4435 4436 combinedInfo.Types.emplace_back(baseFlag);4437 combinedInfo.DevicePointers.emplace_back(4438 mapData.DevicePointers[mapDataIndex]);4439 combinedInfo.Mappers.emplace_back(mapData.Mappers[mapDataIndex]);4440 combinedInfo.Names.emplace_back(LLVM::createMappingInformation(4441 mapData.MapClause[mapDataIndex]->getLoc(), ompBuilder));4442 combinedInfo.BasePointers.emplace_back(mapData.BasePointers[mapDataIndex]);4443 4444 // Calculate size of the parent object being mapped based on the4445 // addresses at runtime, highAddr - lowAddr = size. This of course4446 // doesn't factor in allocated data like pointers, hence the further4447 // processing of members specified by users, or in the case of4448 // Fortran pointers and allocatables, the mapping of the pointed to4449 // data by the descriptor (which itself, is a structure containing4450 // runtime information on the dynamically allocated data).4451 auto parentClause =4452 llvm::cast<omp::MapInfoOp>(mapData.MapClause[mapDataIndex]);4453 llvm::Value *lowAddr, *highAddr;4454 if (!parentClause.getPartialMap()) {4455 lowAddr = builder.CreatePointerCast(mapData.Pointers[mapDataIndex],4456 builder.getPtrTy());4457 highAddr = builder.CreatePointerCast(4458 builder.CreateConstGEP1_32(mapData.BaseType[mapDataIndex],4459 mapData.Pointers[mapDataIndex], 1),4460 builder.getPtrTy());4461 combinedInfo.Pointers.emplace_back(mapData.Pointers[mapDataIndex]);4462 } else {4463 auto mapOp = dyn_cast<omp::MapInfoOp>(mapData.MapClause[mapDataIndex]);4464 int firstMemberIdx = getMapDataMemberIdx(4465 mapData, getFirstOrLastMappedMemberPtr(mapOp, true));4466 lowAddr = builder.CreatePointerCast(mapData.Pointers[firstMemberIdx],4467 builder.getPtrTy());4468 int lastMemberIdx = getMapDataMemberIdx(4469 mapData, getFirstOrLastMappedMemberPtr(mapOp, false));4470 highAddr = builder.CreatePointerCast(4471 builder.CreateGEP(mapData.BaseType[lastMemberIdx],4472 mapData.Pointers[lastMemberIdx], builder.getInt64(1)),4473 builder.getPtrTy());4474 combinedInfo.Pointers.emplace_back(mapData.Pointers[firstMemberIdx]);4475 }4476 4477 llvm::Value *size = builder.CreateIntCast(4478 builder.CreatePtrDiff(builder.getInt8Ty(), highAddr, lowAddr),4479 builder.getInt64Ty(),4480 /*isSigned=*/false);4481 combinedInfo.Sizes.push_back(size);4482 4483 llvm::omp::OpenMPOffloadMappingFlags memberOfFlag =4484 ompBuilder.getMemberOfFlag(combinedInfo.BasePointers.size() - 1);4485 4486 // This creates the initial MEMBER_OF mapping that consists of4487 // the parent/top level container (same as above effectively, except4488 // with a fixed initial compile time size and separate maptype which4489 // indicates the true mape type (tofrom etc.). This parent mapping is4490 // only relevant if the structure in its totality is being mapped,4491 // otherwise the above suffices.4492 if (!parentClause.getPartialMap()) {4493 // TODO: This will need to be expanded to include the whole host of logic4494 // for the map flags that Clang currently supports (e.g. it should do some4495 // further case specific flag modifications). For the moment, it handles4496 // what we support as expected.4497 llvm::omp::OpenMPOffloadMappingFlags mapFlag = mapData.Types[mapDataIndex];4498 bool hasMapClose = (llvm::omp::OpenMPOffloadMappingFlags(mapFlag) &4499 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_CLOSE) ==4500 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_CLOSE;4501 ompBuilder.setCorrectMemberOfFlag(mapFlag, memberOfFlag);4502 4503 if (targetDirective == TargetDirectiveEnumTy::TargetUpdate || hasMapClose) {4504 combinedInfo.Types.emplace_back(mapFlag);4505 combinedInfo.DevicePointers.emplace_back(4506 mapData.DevicePointers[mapDataIndex]);4507 combinedInfo.Names.emplace_back(LLVM::createMappingInformation(4508 mapData.MapClause[mapDataIndex]->getLoc(), ompBuilder));4509 combinedInfo.BasePointers.emplace_back(4510 mapData.BasePointers[mapDataIndex]);4511 combinedInfo.Pointers.emplace_back(mapData.Pointers[mapDataIndex]);4512 combinedInfo.Sizes.emplace_back(mapData.Sizes[mapDataIndex]);4513 combinedInfo.Mappers.emplace_back(mapData.Mappers[mapDataIndex]);4514 } else {4515 llvm::SmallVector<size_t> overlapIdxs;4516 // Find all of the members that "overlap", i.e. occlude other members that4517 // were mapped alongside the parent, e.g. member [0], occludes [0,1] and4518 // [0,2], but not [1,0].4519 getOverlappedMembers(overlapIdxs, parentClause);4520 // We need to make sure the overlapped members are sorted in order of4521 // lowest address to highest address.4522 sortMapIndices(overlapIdxs, parentClause);4523 4524 lowAddr = builder.CreatePointerCast(mapData.Pointers[mapDataIndex],4525 builder.getPtrTy());4526 highAddr = builder.CreatePointerCast(4527 builder.CreateConstGEP1_32(mapData.BaseType[mapDataIndex],4528 mapData.Pointers[mapDataIndex], 1),4529 builder.getPtrTy());4530 4531 // TODO: We may want to skip arrays/array sections in this as Clang does.4532 // It appears to be an optimisation rather than a necessity though,4533 // but this requires further investigation. However, we would have to make4534 // sure to not exclude maps with bounds that ARE pointers, as these are4535 // processed as separate components, i.e. pointer + data.4536 for (auto v : overlapIdxs) {4537 auto mapDataOverlapIdx = getMapDataMemberIdx(4538 mapData,4539 cast<omp::MapInfoOp>(parentClause.getMembers()[v].getDefiningOp()));4540 combinedInfo.Types.emplace_back(mapFlag);4541 combinedInfo.DevicePointers.emplace_back(4542 mapData.DevicePointers[mapDataOverlapIdx]);4543 combinedInfo.Names.emplace_back(LLVM::createMappingInformation(4544 mapData.MapClause[mapDataIndex]->getLoc(), ompBuilder));4545 combinedInfo.BasePointers.emplace_back(4546 mapData.BasePointers[mapDataIndex]);4547 combinedInfo.Mappers.emplace_back(mapData.Mappers[mapDataIndex]);4548 combinedInfo.Pointers.emplace_back(lowAddr);4549 combinedInfo.Sizes.emplace_back(builder.CreateIntCast(4550 builder.CreatePtrDiff(builder.getInt8Ty(),4551 mapData.OriginalValue[mapDataOverlapIdx],4552 lowAddr),4553 builder.getInt64Ty(), /*isSigned=*/true));4554 lowAddr = builder.CreateConstGEP1_32(4555 checkIfPointerMap(llvm::cast<omp::MapInfoOp>(4556 mapData.MapClause[mapDataOverlapIdx]))4557 ? builder.getPtrTy()4558 : mapData.BaseType[mapDataOverlapIdx],4559 mapData.BasePointers[mapDataOverlapIdx], 1);4560 }4561 4562 combinedInfo.Types.emplace_back(mapFlag);4563 combinedInfo.DevicePointers.emplace_back(4564 mapData.DevicePointers[mapDataIndex]);4565 combinedInfo.Names.emplace_back(LLVM::createMappingInformation(4566 mapData.MapClause[mapDataIndex]->getLoc(), ompBuilder));4567 combinedInfo.BasePointers.emplace_back(4568 mapData.BasePointers[mapDataIndex]);4569 combinedInfo.Mappers.emplace_back(mapData.Mappers[mapDataIndex]);4570 combinedInfo.Pointers.emplace_back(lowAddr);4571 combinedInfo.Sizes.emplace_back(builder.CreateIntCast(4572 builder.CreatePtrDiff(builder.getInt8Ty(), highAddr, lowAddr),4573 builder.getInt64Ty(), true));4574 }4575 }4576 return memberOfFlag;4577}4578 4579// This function is intended to add explicit mappings of members4580static void processMapMembersWithParent(4581 LLVM::ModuleTranslation &moduleTranslation, llvm::IRBuilderBase &builder,4582 llvm::OpenMPIRBuilder &ompBuilder, DataLayout &dl, MapInfosTy &combinedInfo,4583 MapInfoData &mapData, uint64_t mapDataIndex,4584 llvm::omp::OpenMPOffloadMappingFlags memberOfFlag,4585 TargetDirectiveEnumTy targetDirective) {4586 assert(!ompBuilder.Config.isTargetDevice() &&4587 "function only supported for host device codegen");4588 4589 auto parentClause =4590 llvm::cast<omp::MapInfoOp>(mapData.MapClause[mapDataIndex]);4591 4592 for (auto mappedMembers : parentClause.getMembers()) {4593 auto memberClause =4594 llvm::cast<omp::MapInfoOp>(mappedMembers.getDefiningOp());4595 int memberDataIdx = getMapDataMemberIdx(mapData, memberClause);4596 4597 assert(memberDataIdx >= 0 && "could not find mapped member of structure");4598 4599 // If we're currently mapping a pointer to a block of data, we must4600 // initially map the pointer, and then attatch/bind the data with a4601 // subsequent map to the pointer. This segment of code generates the4602 // pointer mapping, which can in certain cases be optimised out as Clang4603 // currently does in its lowering. However, for the moment we do not do so,4604 // in part as we currently have substantially less information on the data4605 // being mapped at this stage.4606 if (checkIfPointerMap(memberClause)) {4607 auto mapFlag = convertClauseMapFlags(memberClause.getMapType());4608 mapFlag &= ~llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TARGET_PARAM;4609 mapFlag |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;4610 ompBuilder.setCorrectMemberOfFlag(mapFlag, memberOfFlag);4611 combinedInfo.Types.emplace_back(mapFlag);4612 combinedInfo.DevicePointers.emplace_back(4613 llvm::OpenMPIRBuilder::DeviceInfoTy::None);4614 combinedInfo.Mappers.emplace_back(nullptr);4615 combinedInfo.Names.emplace_back(4616 LLVM::createMappingInformation(memberClause.getLoc(), ompBuilder));4617 combinedInfo.BasePointers.emplace_back(4618 mapData.BasePointers[mapDataIndex]);4619 combinedInfo.Pointers.emplace_back(mapData.BasePointers[memberDataIdx]);4620 combinedInfo.Sizes.emplace_back(builder.getInt64(4621 moduleTranslation.getLLVMModule()->getDataLayout().getPointerSize()));4622 }4623 4624 // Same MemberOfFlag to indicate its link with parent and other members4625 // of.4626 auto mapFlag = convertClauseMapFlags(memberClause.getMapType());4627 mapFlag &= ~llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TARGET_PARAM;4628 mapFlag |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;4629 ompBuilder.setCorrectMemberOfFlag(mapFlag, memberOfFlag);4630 bool isDeclTargetTo = isDeclareTargetTo(parentClause.getVarPtr()4631 ? parentClause.getVarPtr()4632 : parentClause.getVarPtrPtr());4633 if (checkIfPointerMap(memberClause) &&4634 (!isDeclTargetTo ||4635 (targetDirective != TargetDirectiveEnumTy::TargetUpdate &&4636 targetDirective != TargetDirectiveEnumTy::TargetData))) {4637 mapFlag |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_PTR_AND_OBJ;4638 }4639 4640 combinedInfo.Types.emplace_back(mapFlag);4641 combinedInfo.DevicePointers.emplace_back(4642 mapData.DevicePointers[memberDataIdx]);4643 combinedInfo.Mappers.emplace_back(mapData.Mappers[memberDataIdx]);4644 combinedInfo.Names.emplace_back(4645 LLVM::createMappingInformation(memberClause.getLoc(), ompBuilder));4646 uint64_t basePointerIndex =4647 checkIfPointerMap(memberClause) ? memberDataIdx : mapDataIndex;4648 combinedInfo.BasePointers.emplace_back(4649 mapData.BasePointers[basePointerIndex]);4650 combinedInfo.Pointers.emplace_back(mapData.Pointers[memberDataIdx]);4651 4652 llvm::Value *size = mapData.Sizes[memberDataIdx];4653 if (checkIfPointerMap(memberClause)) {4654 size = builder.CreateSelect(4655 builder.CreateIsNull(mapData.Pointers[memberDataIdx]),4656 builder.getInt64(0), size);4657 }4658 4659 combinedInfo.Sizes.emplace_back(size);4660 }4661}4662 4663static void processIndividualMap(MapInfoData &mapData, size_t mapDataIdx,4664 MapInfosTy &combinedInfo,4665 TargetDirectiveEnumTy targetDirective,4666 int mapDataParentIdx = -1) {4667 // Declare Target Mappings are excluded from being marked as4668 // OMP_MAP_TARGET_PARAM as they are not passed as parameters, they're4669 // marked with OMP_MAP_PTR_AND_OBJ instead.4670 auto mapFlag = mapData.Types[mapDataIdx];4671 auto mapInfoOp = llvm::cast<omp::MapInfoOp>(mapData.MapClause[mapDataIdx]);4672 4673 bool isPtrTy = checkIfPointerMap(mapInfoOp);4674 if (isPtrTy)4675 mapFlag |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_PTR_AND_OBJ;4676 4677 if (targetDirective == TargetDirectiveEnumTy::Target &&4678 !mapData.IsDeclareTarget[mapDataIdx])4679 mapFlag |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TARGET_PARAM;4680 4681 if (mapInfoOp.getMapCaptureType() == omp::VariableCaptureKind::ByCopy &&4682 !isPtrTy)4683 mapFlag |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_LITERAL;4684 4685 // if we're provided a mapDataParentIdx, then the data being mapped is4686 // part of a larger object (in a parent <-> member mapping) and in this4687 // case our BasePointer should be the parent.4688 if (mapDataParentIdx >= 0)4689 combinedInfo.BasePointers.emplace_back(4690 mapData.BasePointers[mapDataParentIdx]);4691 else4692 combinedInfo.BasePointers.emplace_back(mapData.BasePointers[mapDataIdx]);4693 4694 combinedInfo.Pointers.emplace_back(mapData.Pointers[mapDataIdx]);4695 combinedInfo.DevicePointers.emplace_back(mapData.DevicePointers[mapDataIdx]);4696 combinedInfo.Mappers.emplace_back(mapData.Mappers[mapDataIdx]);4697 combinedInfo.Names.emplace_back(mapData.Names[mapDataIdx]);4698 combinedInfo.Types.emplace_back(mapFlag);4699 combinedInfo.Sizes.emplace_back(mapData.Sizes[mapDataIdx]);4700}4701 4702static void processMapWithMembersOf(LLVM::ModuleTranslation &moduleTranslation,4703 llvm::IRBuilderBase &builder,4704 llvm::OpenMPIRBuilder &ompBuilder,4705 DataLayout &dl, MapInfosTy &combinedInfo,4706 MapInfoData &mapData, uint64_t mapDataIndex,4707 TargetDirectiveEnumTy targetDirective) {4708 assert(!ompBuilder.Config.isTargetDevice() &&4709 "function only supported for host device codegen");4710 4711 auto parentClause =4712 llvm::cast<omp::MapInfoOp>(mapData.MapClause[mapDataIndex]);4713 4714 // If we have a partial map (no parent referenced in the map clauses of the4715 // directive, only members) and only a single member, we do not need to bind4716 // the map of the member to the parent, we can pass the member separately.4717 if (parentClause.getMembers().size() == 1 && parentClause.getPartialMap()) {4718 auto memberClause = llvm::cast<omp::MapInfoOp>(4719 parentClause.getMembers()[0].getDefiningOp());4720 int memberDataIdx = getMapDataMemberIdx(mapData, memberClause);4721 // Note: Clang treats arrays with explicit bounds that fall into this4722 // category as a parent with map case, however, it seems this isn't a4723 // requirement, and processing them as an individual map is fine. So,4724 // we will handle them as individual maps for the moment, as it's4725 // difficult for us to check this as we always require bounds to be4726 // specified currently and it's also marginally more optimal (single4727 // map rather than two). The difference may come from the fact that4728 // Clang maps array without bounds as pointers (which we do not4729 // currently do), whereas we treat them as arrays in all cases4730 // currently.4731 processIndividualMap(mapData, memberDataIdx, combinedInfo, targetDirective,4732 mapDataIndex);4733 return;4734 }4735 4736 llvm::omp::OpenMPOffloadMappingFlags memberOfParentFlag =4737 mapParentWithMembers(moduleTranslation, builder, ompBuilder, dl,4738 combinedInfo, mapData, mapDataIndex,4739 targetDirective);4740 processMapMembersWithParent(moduleTranslation, builder, ompBuilder, dl,4741 combinedInfo, mapData, mapDataIndex,4742 memberOfParentFlag, targetDirective);4743}4744 4745// This is a variation on Clang's GenerateOpenMPCapturedVars, which4746// generates different operation (e.g. load/store) combinations for4747// arguments to the kernel, based on map capture kinds which are then4748// utilised in the combinedInfo in place of the original Map value.4749static void4750createAlteredByCaptureMap(MapInfoData &mapData,4751 LLVM::ModuleTranslation &moduleTranslation,4752 llvm::IRBuilderBase &builder) {4753 assert(!moduleTranslation.getOpenMPBuilder()->Config.isTargetDevice() &&4754 "function only supported for host device codegen");4755 for (size_t i = 0; i < mapData.MapClause.size(); ++i) {4756 // if it's declare target, skip it, it's handled separately.4757 if (!mapData.IsDeclareTarget[i]) {4758 auto mapOp = cast<omp::MapInfoOp>(mapData.MapClause[i]);4759 omp::VariableCaptureKind captureKind = mapOp.getMapCaptureType();4760 bool isPtrTy = checkIfPointerMap(mapOp);4761 4762 // Currently handles array sectioning lowerbound case, but more4763 // logic may be required in the future. Clang invokes EmitLValue,4764 // which has specialised logic for special Clang types such as user4765 // defines, so it is possible we will have to extend this for4766 // structures or other complex types. As the general idea is that this4767 // function mimics some of the logic from Clang that we require for4768 // kernel argument passing from host -> device.4769 switch (captureKind) {4770 case omp::VariableCaptureKind::ByRef: {4771 llvm::Value *newV = mapData.Pointers[i];4772 std::vector<llvm::Value *> offsetIdx = calculateBoundsOffset(4773 moduleTranslation, builder, mapData.BaseType[i]->isArrayTy(),4774 mapOp.getBounds());4775 if (isPtrTy)4776 newV = builder.CreateLoad(builder.getPtrTy(), newV);4777 4778 if (!offsetIdx.empty())4779 newV = builder.CreateInBoundsGEP(mapData.BaseType[i], newV, offsetIdx,4780 "array_offset");4781 mapData.Pointers[i] = newV;4782 } break;4783 case omp::VariableCaptureKind::ByCopy: {4784 llvm::Type *type = mapData.BaseType[i];4785 llvm::Value *newV;4786 if (mapData.Pointers[i]->getType()->isPointerTy())4787 newV = builder.CreateLoad(type, mapData.Pointers[i]);4788 else4789 newV = mapData.Pointers[i];4790 4791 if (!isPtrTy) {4792 auto curInsert = builder.saveIP();4793 llvm::DebugLoc DbgLoc = builder.getCurrentDebugLocation();4794 builder.restoreIP(findAllocaInsertPoint(builder, moduleTranslation));4795 auto *memTempAlloc =4796 builder.CreateAlloca(builder.getPtrTy(), nullptr, ".casted");4797 builder.SetCurrentDebugLocation(DbgLoc);4798 builder.restoreIP(curInsert);4799 4800 builder.CreateStore(newV, memTempAlloc);4801 newV = builder.CreateLoad(builder.getPtrTy(), memTempAlloc);4802 }4803 4804 mapData.Pointers[i] = newV;4805 mapData.BasePointers[i] = newV;4806 } break;4807 case omp::VariableCaptureKind::This:4808 case omp::VariableCaptureKind::VLAType:4809 mapData.MapClause[i]->emitOpError("Unhandled capture kind");4810 break;4811 }4812 }4813 }4814}4815 4816// Generate all map related information and fill the combinedInfo.4817static void genMapInfos(llvm::IRBuilderBase &builder,4818 LLVM::ModuleTranslation &moduleTranslation,4819 DataLayout &dl, MapInfosTy &combinedInfo,4820 MapInfoData &mapData,4821 TargetDirectiveEnumTy targetDirective) {4822 assert(!moduleTranslation.getOpenMPBuilder()->Config.isTargetDevice() &&4823 "function only supported for host device codegen");4824 // We wish to modify some of the methods in which arguments are4825 // passed based on their capture type by the target region, this can4826 // involve generating new loads and stores, which changes the4827 // MLIR value to LLVM value mapping, however, we only wish to do this4828 // locally for the current function/target and also avoid altering4829 // ModuleTranslation, so we remap the base pointer or pointer stored4830 // in the map infos corresponding MapInfoData, which is later accessed4831 // by genMapInfos and createTarget to help generate the kernel and4832 // kernel arg structure. It primarily becomes relevant in cases like4833 // bycopy, or byref range'd arrays. In the default case, we simply4834 // pass thee pointer byref as both basePointer and pointer.4835 createAlteredByCaptureMap(mapData, moduleTranslation, builder);4836 4837 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();4838 4839 // We operate under the assumption that all vectors that are4840 // required in MapInfoData are of equal lengths (either filled with4841 // default constructed data or appropiate information) so we can4842 // utilise the size from any component of MapInfoData, if we can't4843 // something is missing from the initial MapInfoData construction.4844 for (size_t i = 0; i < mapData.MapClause.size(); ++i) {4845 // NOTE/TODO: We currently do not support arbitrary depth record4846 // type mapping.4847 if (mapData.IsAMember[i])4848 continue;4849 4850 auto mapInfoOp = dyn_cast<omp::MapInfoOp>(mapData.MapClause[i]);4851 if (!mapInfoOp.getMembers().empty()) {4852 processMapWithMembersOf(moduleTranslation, builder, *ompBuilder, dl,4853 combinedInfo, mapData, i, targetDirective);4854 continue;4855 }4856 4857 processIndividualMap(mapData, i, combinedInfo, targetDirective);4858 }4859}4860 4861static llvm::Expected<llvm::Function *>4862emitUserDefinedMapper(Operation *declMapperOp, llvm::IRBuilderBase &builder,4863 LLVM::ModuleTranslation &moduleTranslation,4864 llvm::StringRef mapperFuncName,4865 TargetDirectiveEnumTy targetDirective);4866 4867static llvm::Expected<llvm::Function *>4868getOrCreateUserDefinedMapperFunc(Operation *op, llvm::IRBuilderBase &builder,4869 LLVM::ModuleTranslation &moduleTranslation,4870 TargetDirectiveEnumTy targetDirective) {4871 assert(!moduleTranslation.getOpenMPBuilder()->Config.isTargetDevice() &&4872 "function only supported for host device codegen");4873 auto declMapperOp = cast<omp::DeclareMapperOp>(op);4874 std::string mapperFuncName =4875 moduleTranslation.getOpenMPBuilder()->createPlatformSpecificName(4876 {"omp_mapper", declMapperOp.getSymName()});4877 4878 if (auto *lookupFunc = moduleTranslation.lookupFunction(mapperFuncName))4879 return lookupFunc;4880 4881 return emitUserDefinedMapper(declMapperOp, builder, moduleTranslation,4882 mapperFuncName, targetDirective);4883}4884 4885static llvm::Expected<llvm::Function *>4886emitUserDefinedMapper(Operation *op, llvm::IRBuilderBase &builder,4887 LLVM::ModuleTranslation &moduleTranslation,4888 llvm::StringRef mapperFuncName,4889 TargetDirectiveEnumTy targetDirective) {4890 assert(!moduleTranslation.getOpenMPBuilder()->Config.isTargetDevice() &&4891 "function only supported for host device codegen");4892 auto declMapperOp = cast<omp::DeclareMapperOp>(op);4893 auto declMapperInfoOp = declMapperOp.getDeclareMapperInfo();4894 DataLayout dl = DataLayout(declMapperOp->getParentOfType<ModuleOp>());4895 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();4896 llvm::Type *varType = moduleTranslation.convertType(declMapperOp.getType());4897 SmallVector<Value> mapVars = declMapperInfoOp.getMapVars();4898 4899 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;4900 4901 // Fill up the arrays with all the mapped variables.4902 MapInfosTy combinedInfo;4903 auto genMapInfoCB =4904 [&](InsertPointTy codeGenIP, llvm::Value *ptrPHI,4905 llvm::Value *unused2) -> llvm::OpenMPIRBuilder::MapInfosOrErrorTy {4906 builder.restoreIP(codeGenIP);4907 moduleTranslation.mapValue(declMapperOp.getSymVal(), ptrPHI);4908 moduleTranslation.mapBlock(&declMapperOp.getRegion().front(),4909 builder.GetInsertBlock());4910 if (failed(moduleTranslation.convertBlock(declMapperOp.getRegion().front(),4911 /*ignoreArguments=*/true,4912 builder)))4913 return llvm::make_error<PreviouslyReportedError>();4914 MapInfoData mapData;4915 collectMapDataFromMapOperands(mapData, mapVars, moduleTranslation, dl,4916 builder);4917 genMapInfos(builder, moduleTranslation, dl, combinedInfo, mapData,4918 targetDirective);4919 4920 // Drop the mapping that is no longer necessary so that the same region4921 // can be processed multiple times.4922 moduleTranslation.forgetMapping(declMapperOp.getRegion());4923 return combinedInfo;4924 };4925 4926 auto customMapperCB = [&](unsigned i) -> llvm::Expected<llvm::Function *> {4927 if (!combinedInfo.Mappers[i])4928 return nullptr;4929 return getOrCreateUserDefinedMapperFunc(combinedInfo.Mappers[i], builder,4930 moduleTranslation, targetDirective);4931 };4932 4933 llvm::Expected<llvm::Function *> newFn = ompBuilder->emitUserDefinedMapper(4934 genMapInfoCB, varType, mapperFuncName, customMapperCB);4935 if (!newFn)4936 return newFn.takeError();4937 moduleTranslation.mapFunction(mapperFuncName, *newFn);4938 return *newFn;4939}4940 4941static LogicalResult4942convertOmpTargetData(Operation *op, llvm::IRBuilderBase &builder,4943 LLVM::ModuleTranslation &moduleTranslation) {4944 llvm::Value *ifCond = nullptr;4945 int64_t deviceID = llvm::omp::OMP_DEVICEID_UNDEF;4946 SmallVector<Value> mapVars;4947 SmallVector<Value> useDevicePtrVars;4948 SmallVector<Value> useDeviceAddrVars;4949 llvm::omp::RuntimeFunction RTLFn;4950 DataLayout DL = DataLayout(op->getParentOfType<ModuleOp>());4951 TargetDirectiveEnumTy targetDirective = getTargetDirectiveEnumTyFromOp(op);4952 4953 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();4954 llvm::OpenMPIRBuilder::TargetDataInfo info(4955 /*RequiresDevicePointerInfo=*/true,4956 /*SeparateBeginEndCalls=*/true);4957 bool isTargetDevice = ompBuilder->Config.isTargetDevice();4958 bool isOffloadEntry =4959 isTargetDevice || !ompBuilder->Config.TargetTriples.empty();4960 4961 LogicalResult result =4962 llvm::TypeSwitch<Operation *, LogicalResult>(op)4963 .Case([&](omp::TargetDataOp dataOp) {4964 if (failed(checkImplementationStatus(*dataOp)))4965 return failure();4966 4967 if (auto ifVar = dataOp.getIfExpr())4968 ifCond = moduleTranslation.lookupValue(ifVar);4969 4970 if (auto devId = dataOp.getDevice())4971 if (auto constOp = devId.getDefiningOp<LLVM::ConstantOp>())4972 if (auto intAttr = dyn_cast<IntegerAttr>(constOp.getValue()))4973 deviceID = intAttr.getInt();4974 4975 mapVars = dataOp.getMapVars();4976 useDevicePtrVars = dataOp.getUseDevicePtrVars();4977 useDeviceAddrVars = dataOp.getUseDeviceAddrVars();4978 return success();4979 })4980 .Case([&](omp::TargetEnterDataOp enterDataOp) -> LogicalResult {4981 if (failed(checkImplementationStatus(*enterDataOp)))4982 return failure();4983 4984 if (auto ifVar = enterDataOp.getIfExpr())4985 ifCond = moduleTranslation.lookupValue(ifVar);4986 4987 if (auto devId = enterDataOp.getDevice())4988 if (auto constOp = devId.getDefiningOp<LLVM::ConstantOp>())4989 if (auto intAttr = dyn_cast<IntegerAttr>(constOp.getValue()))4990 deviceID = intAttr.getInt();4991 RTLFn =4992 enterDataOp.getNowait()4993 ? llvm::omp::OMPRTL___tgt_target_data_begin_nowait_mapper4994 : llvm::omp::OMPRTL___tgt_target_data_begin_mapper;4995 mapVars = enterDataOp.getMapVars();4996 info.HasNoWait = enterDataOp.getNowait();4997 return success();4998 })4999 .Case([&](omp::TargetExitDataOp exitDataOp) -> LogicalResult {5000 if (failed(checkImplementationStatus(*exitDataOp)))5001 return failure();5002 5003 if (auto ifVar = exitDataOp.getIfExpr())5004 ifCond = moduleTranslation.lookupValue(ifVar);5005 5006 if (auto devId = exitDataOp.getDevice())5007 if (auto constOp = devId.getDefiningOp<LLVM::ConstantOp>())5008 if (auto intAttr = dyn_cast<IntegerAttr>(constOp.getValue()))5009 deviceID = intAttr.getInt();5010 5011 RTLFn = exitDataOp.getNowait()5012 ? llvm::omp::OMPRTL___tgt_target_data_end_nowait_mapper5013 : llvm::omp::OMPRTL___tgt_target_data_end_mapper;5014 mapVars = exitDataOp.getMapVars();5015 info.HasNoWait = exitDataOp.getNowait();5016 return success();5017 })5018 .Case([&](omp::TargetUpdateOp updateDataOp) -> LogicalResult {5019 if (failed(checkImplementationStatus(*updateDataOp)))5020 return failure();5021 5022 if (auto ifVar = updateDataOp.getIfExpr())5023 ifCond = moduleTranslation.lookupValue(ifVar);5024 5025 if (auto devId = updateDataOp.getDevice())5026 if (auto constOp = devId.getDefiningOp<LLVM::ConstantOp>())5027 if (auto intAttr = dyn_cast<IntegerAttr>(constOp.getValue()))5028 deviceID = intAttr.getInt();5029 5030 RTLFn =5031 updateDataOp.getNowait()5032 ? llvm::omp::OMPRTL___tgt_target_data_update_nowait_mapper5033 : llvm::omp::OMPRTL___tgt_target_data_update_mapper;5034 mapVars = updateDataOp.getMapVars();5035 info.HasNoWait = updateDataOp.getNowait();5036 return success();5037 })5038 .DefaultUnreachable("unexpected operation");5039 5040 if (failed(result))5041 return failure();5042 // Pretend we have IF(false) if we're not doing offload.5043 if (!isOffloadEntry)5044 ifCond = builder.getFalse();5045 5046 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;5047 MapInfoData mapData;5048 collectMapDataFromMapOperands(mapData, mapVars, moduleTranslation, DL,5049 builder, useDevicePtrVars, useDeviceAddrVars);5050 5051 // Fill up the arrays with all the mapped variables.5052 MapInfosTy combinedInfo;5053 auto genMapInfoCB = [&](InsertPointTy codeGenIP) -> MapInfosTy & {5054 builder.restoreIP(codeGenIP);5055 genMapInfos(builder, moduleTranslation, DL, combinedInfo, mapData,5056 targetDirective);5057 return combinedInfo;5058 };5059 5060 // Define a lambda to apply mappings between use_device_addr and5061 // use_device_ptr base pointers, and their associated block arguments.5062 auto mapUseDevice =5063 [&moduleTranslation](5064 llvm::OpenMPIRBuilder::DeviceInfoTy type,5065 llvm::ArrayRef<BlockArgument> blockArgs,5066 llvm::SmallVectorImpl<Value> &useDeviceVars, MapInfoData &mapInfoData,5067 llvm::function_ref<llvm::Value *(llvm::Value *)> mapper = nullptr) {5068 for (auto [arg, useDevVar] :5069 llvm::zip_equal(blockArgs, useDeviceVars)) {5070 5071 auto getMapBasePtr = [](omp::MapInfoOp mapInfoOp) {5072 return mapInfoOp.getVarPtrPtr() ? mapInfoOp.getVarPtrPtr()5073 : mapInfoOp.getVarPtr();5074 };5075 5076 auto useDevMap = cast<omp::MapInfoOp>(useDevVar.getDefiningOp());5077 for (auto [mapClause, devicePointer, basePointer] : llvm::zip_equal(5078 mapInfoData.MapClause, mapInfoData.DevicePointers,5079 mapInfoData.BasePointers)) {5080 auto mapOp = cast<omp::MapInfoOp>(mapClause);5081 if (getMapBasePtr(mapOp) != getMapBasePtr(useDevMap) ||5082 devicePointer != type)5083 continue;5084 5085 if (llvm::Value *devPtrInfoMap =5086 mapper ? mapper(basePointer) : basePointer) {5087 moduleTranslation.mapValue(arg, devPtrInfoMap);5088 break;5089 }5090 }5091 }5092 };5093 5094 using BodyGenTy = llvm::OpenMPIRBuilder::BodyGenTy;5095 auto bodyGenCB = [&](InsertPointTy codeGenIP, BodyGenTy bodyGenType)5096 -> llvm::OpenMPIRBuilder::InsertPointOrErrorTy {5097 // We must always restoreIP regardless of doing anything the caller5098 // does not restore it, leading to incorrect (no) branch generation.5099 builder.restoreIP(codeGenIP);5100 assert(isa<omp::TargetDataOp>(op) &&5101 "BodyGen requested for non TargetDataOp");5102 auto blockArgIface = cast<omp::BlockArgOpenMPOpInterface>(op);5103 Region ®ion = cast<omp::TargetDataOp>(op).getRegion();5104 switch (bodyGenType) {5105 case BodyGenTy::Priv:5106 // Check if any device ptr/addr info is available5107 if (!info.DevicePtrInfoMap.empty()) {5108 mapUseDevice(llvm::OpenMPIRBuilder::DeviceInfoTy::Address,5109 blockArgIface.getUseDeviceAddrBlockArgs(),5110 useDeviceAddrVars, mapData,5111 [&](llvm::Value *basePointer) -> llvm::Value * {5112 if (!info.DevicePtrInfoMap[basePointer].second)5113 return nullptr;5114 return builder.CreateLoad(5115 builder.getPtrTy(),5116 info.DevicePtrInfoMap[basePointer].second);5117 });5118 mapUseDevice(llvm::OpenMPIRBuilder::DeviceInfoTy::Pointer,5119 blockArgIface.getUseDevicePtrBlockArgs(), useDevicePtrVars,5120 mapData, [&](llvm::Value *basePointer) {5121 return info.DevicePtrInfoMap[basePointer].second;5122 });5123 5124 if (failed(inlineConvertOmpRegions(region, "omp.data.region", builder,5125 moduleTranslation)))5126 return llvm::make_error<PreviouslyReportedError>();5127 }5128 break;5129 case BodyGenTy::DupNoPriv:5130 if (info.DevicePtrInfoMap.empty()) {5131 // For host device we still need to do the mapping for codegen,5132 // otherwise it may try to lookup a missing value.5133 if (!ompBuilder->Config.IsTargetDevice.value_or(false)) {5134 mapUseDevice(llvm::OpenMPIRBuilder::DeviceInfoTy::Address,5135 blockArgIface.getUseDeviceAddrBlockArgs(),5136 useDeviceAddrVars, mapData);5137 mapUseDevice(llvm::OpenMPIRBuilder::DeviceInfoTy::Pointer,5138 blockArgIface.getUseDevicePtrBlockArgs(),5139 useDevicePtrVars, mapData);5140 }5141 }5142 break;5143 case BodyGenTy::NoPriv:5144 // If device info is available then region has already been generated5145 if (info.DevicePtrInfoMap.empty()) {5146 // For device pass, if use_device_ptr(addr) mappings were present,5147 // we need to link them here before codegen.5148 if (ompBuilder->Config.IsTargetDevice.value_or(false)) {5149 mapUseDevice(llvm::OpenMPIRBuilder::DeviceInfoTy::Address,5150 blockArgIface.getUseDeviceAddrBlockArgs(),5151 useDeviceAddrVars, mapData);5152 mapUseDevice(llvm::OpenMPIRBuilder::DeviceInfoTy::Pointer,5153 blockArgIface.getUseDevicePtrBlockArgs(),5154 useDevicePtrVars, mapData);5155 }5156 5157 if (failed(inlineConvertOmpRegions(region, "omp.data.region", builder,5158 moduleTranslation)))5159 return llvm::make_error<PreviouslyReportedError>();5160 }5161 break;5162 }5163 return builder.saveIP();5164 };5165 5166 auto customMapperCB =5167 [&](unsigned int i) -> llvm::Expected<llvm::Function *> {5168 if (!combinedInfo.Mappers[i])5169 return nullptr;5170 info.HasMapper = true;5171 return getOrCreateUserDefinedMapperFunc(combinedInfo.Mappers[i], builder,5172 moduleTranslation, targetDirective);5173 };5174 5175 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);5176 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =5177 findAllocaInsertPoint(builder, moduleTranslation);5178 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP = [&]() {5179 if (isa<omp::TargetDataOp>(op))5180 return ompBuilder->createTargetData(ompLoc, allocaIP, builder.saveIP(),5181 builder.getInt64(deviceID), ifCond,5182 info, genMapInfoCB, customMapperCB,5183 /*MapperFunc=*/nullptr, bodyGenCB,5184 /*DeviceAddrCB=*/nullptr);5185 return ompBuilder->createTargetData(5186 ompLoc, allocaIP, builder.saveIP(), builder.getInt64(deviceID), ifCond,5187 info, genMapInfoCB, customMapperCB, &RTLFn);5188 }();5189 5190 if (failed(handleError(afterIP, *op)))5191 return failure();5192 5193 builder.restoreIP(*afterIP);5194 return success();5195}5196 5197static LogicalResult5198convertOmpDistribute(Operation &opInst, llvm::IRBuilderBase &builder,5199 LLVM::ModuleTranslation &moduleTranslation) {5200 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();5201 auto distributeOp = cast<omp::DistributeOp>(opInst);5202 if (failed(checkImplementationStatus(opInst)))5203 return failure();5204 5205 /// Process teams op reduction in distribute if the reduction is contained in5206 /// the distribute op.5207 omp::TeamsOp teamsOp = opInst.getParentOfType<omp::TeamsOp>();5208 bool doDistributeReduction =5209 teamsOp ? teamsReductionContainedInDistribute(teamsOp) : false;5210 5211 DenseMap<Value, llvm::Value *> reductionVariableMap;5212 unsigned numReductionVars = teamsOp ? teamsOp.getNumReductionVars() : 0;5213 SmallVector<omp::DeclareReductionOp> reductionDecls;5214 SmallVector<llvm::Value *> privateReductionVariables(numReductionVars);5215 llvm::ArrayRef<bool> isByRef;5216 5217 if (doDistributeReduction) {5218 isByRef = getIsByRef(teamsOp.getReductionByref());5219 assert(isByRef.size() == teamsOp.getNumReductionVars());5220 5221 collectReductionDecls(teamsOp, reductionDecls);5222 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =5223 findAllocaInsertPoint(builder, moduleTranslation);5224 5225 MutableArrayRef<BlockArgument> reductionArgs =5226 llvm::cast<omp::BlockArgOpenMPOpInterface>(*teamsOp)5227 .getReductionBlockArgs();5228 5229 if (failed(allocAndInitializeReductionVars(5230 teamsOp, reductionArgs, builder, moduleTranslation, allocaIP,5231 reductionDecls, privateReductionVariables, reductionVariableMap,5232 isByRef)))5233 return failure();5234 }5235 5236 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;5237 auto bodyGenCB = [&](InsertPointTy allocaIP,5238 InsertPointTy codeGenIP) -> llvm::Error {5239 // Save the alloca insertion point on ModuleTranslation stack for use in5240 // nested regions.5241 LLVM::ModuleTranslation::SaveStack<OpenMPAllocaStackFrame> frame(5242 moduleTranslation, allocaIP);5243 5244 // DistributeOp has only one region associated with it.5245 builder.restoreIP(codeGenIP);5246 PrivateVarsInfo privVarsInfo(distributeOp);5247 5248 llvm::Expected<llvm::BasicBlock *> afterAllocas =5249 allocatePrivateVars(builder, moduleTranslation, privVarsInfo, allocaIP);5250 if (handleError(afterAllocas, opInst).failed())5251 return llvm::make_error<PreviouslyReportedError>();5252 5253 if (handleError(initPrivateVars(builder, moduleTranslation, privVarsInfo),5254 opInst)5255 .failed())5256 return llvm::make_error<PreviouslyReportedError>();5257 5258 if (failed(copyFirstPrivateVars(5259 distributeOp, builder, moduleTranslation, privVarsInfo.mlirVars,5260 privVarsInfo.llvmVars, privVarsInfo.privatizers,5261 distributeOp.getPrivateNeedsBarrier())))5262 return llvm::make_error<PreviouslyReportedError>();5263 5264 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();5265 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);5266 llvm::Expected<llvm::BasicBlock *> regionBlock =5267 convertOmpOpRegions(distributeOp.getRegion(), "omp.distribute.region",5268 builder, moduleTranslation);5269 if (!regionBlock)5270 return regionBlock.takeError();5271 builder.SetInsertPoint(*regionBlock, (*regionBlock)->begin());5272 5273 // Skip applying a workshare loop below when translating 'distribute5274 // parallel do' (it's been already handled by this point while translating5275 // the nested omp.wsloop).5276 if (!isa_and_present<omp::WsloopOp>(distributeOp.getNestedWrapper())) {5277 // TODO: Add support for clauses which are valid for DISTRIBUTE5278 // constructs. Static schedule is the default.5279 bool hasDistSchedule = distributeOp.getDistScheduleStatic();5280 auto schedule = hasDistSchedule ? omp::ClauseScheduleKind::Distribute5281 : omp::ClauseScheduleKind::Static;5282 // dist_schedule clauses are ordered - otherise this should be false5283 bool isOrdered = hasDistSchedule;5284 std::optional<omp::ScheduleModifier> scheduleMod;5285 bool isSimd = false;5286 llvm::omp::WorksharingLoopType workshareLoopType =5287 llvm::omp::WorksharingLoopType::DistributeStaticLoop;5288 bool loopNeedsBarrier = false;5289 llvm::Value *chunk = moduleTranslation.lookupValue(5290 distributeOp.getDistScheduleChunkSize());5291 llvm::CanonicalLoopInfo *loopInfo =5292 findCurrentLoopInfo(moduleTranslation);5293 llvm::OpenMPIRBuilder::InsertPointOrErrorTy wsloopIP =5294 ompBuilder->applyWorkshareLoop(5295 ompLoc.DL, loopInfo, allocaIP, loopNeedsBarrier,5296 convertToScheduleKind(schedule), chunk, isSimd,5297 scheduleMod == omp::ScheduleModifier::monotonic,5298 scheduleMod == omp::ScheduleModifier::nonmonotonic, isOrdered,5299 workshareLoopType, false, hasDistSchedule, chunk);5300 5301 if (!wsloopIP)5302 return wsloopIP.takeError();5303 }5304 if (failed(cleanupPrivateVars(builder, moduleTranslation,5305 distributeOp.getLoc(), privVarsInfo.llvmVars,5306 privVarsInfo.privatizers)))5307 return llvm::make_error<PreviouslyReportedError>();5308 5309 return llvm::Error::success();5310 };5311 5312 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =5313 findAllocaInsertPoint(builder, moduleTranslation);5314 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);5315 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =5316 ompBuilder->createDistribute(ompLoc, allocaIP, bodyGenCB);5317 5318 if (failed(handleError(afterIP, opInst)))5319 return failure();5320 5321 builder.restoreIP(*afterIP);5322 5323 if (doDistributeReduction) {5324 // Process the reductions if required.5325 return createReductionsAndCleanup(5326 teamsOp, builder, moduleTranslation, allocaIP, reductionDecls,5327 privateReductionVariables, isByRef,5328 /*isNoWait*/ false, /*isTeamsReduction*/ true);5329 }5330 return success();5331}5332 5333/// Lowers the FlagsAttr which is applied to the module on the device5334/// pass when offloading, this attribute contains OpenMP RTL globals that can5335/// be passed as flags to the frontend, otherwise they are set to default5336static LogicalResult5337convertFlagsAttr(Operation *op, mlir::omp::FlagsAttr attribute,5338 LLVM::ModuleTranslation &moduleTranslation) {5339 if (!cast<mlir::ModuleOp>(op))5340 return failure();5341 5342 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();5343 5344 ompBuilder->M.addModuleFlag(llvm::Module::Max, "openmp-device",5345 attribute.getOpenmpDeviceVersion());5346 5347 if (attribute.getNoGpuLib())5348 return success();5349 5350 ompBuilder->createGlobalFlag(5351 attribute.getDebugKind() /*LangOpts().OpenMPTargetDebug*/,5352 "__omp_rtl_debug_kind");5353 ompBuilder->createGlobalFlag(5354 attribute5355 .getAssumeTeamsOversubscription() /*LangOpts().OpenMPTeamSubscription*/5356 ,5357 "__omp_rtl_assume_teams_oversubscription");5358 ompBuilder->createGlobalFlag(5359 attribute5360 .getAssumeThreadsOversubscription() /*LangOpts().OpenMPThreadSubscription*/5361 ,5362 "__omp_rtl_assume_threads_oversubscription");5363 ompBuilder->createGlobalFlag(5364 attribute.getAssumeNoThreadState() /*LangOpts().OpenMPNoThreadState*/,5365 "__omp_rtl_assume_no_thread_state");5366 ompBuilder->createGlobalFlag(5367 attribute5368 .getAssumeNoNestedParallelism() /*LangOpts().OpenMPNoNestedParallelism*/5369 ,5370 "__omp_rtl_assume_no_nested_parallelism");5371 return success();5372}5373 5374static void getTargetEntryUniqueInfo(llvm::TargetRegionEntryInfo &targetInfo,5375 omp::TargetOp targetOp,5376 llvm::StringRef parentName = "") {5377 auto fileLoc = targetOp.getLoc()->findInstanceOf<FileLineColLoc>();5378 5379 assert(fileLoc && "No file found from location");5380 StringRef fileName = fileLoc.getFilename().getValue();5381 5382 llvm::sys::fs::UniqueID id;5383 uint64_t line = fileLoc.getLine();5384 if (auto ec = llvm::sys::fs::getUniqueID(fileName, id)) {5385 size_t fileHash = llvm::hash_value(fileName.str());5386 size_t deviceId = 0xdeadf17e;5387 targetInfo =5388 llvm::TargetRegionEntryInfo(parentName, deviceId, fileHash, line);5389 } else {5390 targetInfo = llvm::TargetRegionEntryInfo(parentName, id.getDevice(),5391 id.getFile(), line);5392 }5393}5394 5395static void5396handleDeclareTargetMapVar(MapInfoData &mapData,5397 LLVM::ModuleTranslation &moduleTranslation,5398 llvm::IRBuilderBase &builder, llvm::Function *func) {5399 assert(moduleTranslation.getOpenMPBuilder()->Config.isTargetDevice() &&5400 "function only supported for target device codegen");5401 llvm::IRBuilderBase::InsertPointGuard guard(builder);5402 for (size_t i = 0; i < mapData.MapClause.size(); ++i) {5403 // In the case of declare target mapped variables, the basePointer is5404 // the reference pointer generated by the convertDeclareTargetAttr5405 // method. Whereas the kernelValue is the original variable, so for5406 // the device we must replace all uses of this original global variable5407 // (stored in kernelValue) with the reference pointer (stored in5408 // basePointer for declare target mapped variables), as for device the5409 // data is mapped into this reference pointer and should be loaded5410 // from it, the original variable is discarded. On host both exist and5411 // metadata is generated (elsewhere in the convertDeclareTargetAttr)5412 // function to link the two variables in the runtime and then both the5413 // reference pointer and the pointer are assigned in the kernel argument5414 // structure for the host.5415 if (mapData.IsDeclareTarget[i]) {5416 // If the original map value is a constant, then we have to make sure all5417 // of it's uses within the current kernel/function that we are going to5418 // rewrite are converted to instructions, as we will be altering the old5419 // use (OriginalValue) from a constant to an instruction, which will be5420 // illegal and ICE the compiler if the user is a constant expression of5421 // some kind e.g. a constant GEP.5422 if (auto *constant = dyn_cast<llvm::Constant>(mapData.OriginalValue[i]))5423 convertUsersOfConstantsToInstructions(constant, func, false);5424 5425 // The users iterator will get invalidated if we modify an element,5426 // so we populate this vector of uses to alter each user on an5427 // individual basis to emit its own load (rather than one load for5428 // all).5429 llvm::SmallVector<llvm::User *> userVec;5430 for (llvm::User *user : mapData.OriginalValue[i]->users())5431 userVec.push_back(user);5432 5433 for (llvm::User *user : userVec) {5434 if (auto *insn = dyn_cast<llvm::Instruction>(user)) {5435 if (insn->getFunction() == func) {5436 auto mapOp = cast<omp::MapInfoOp>(mapData.MapClause[i]);5437 llvm::Value *substitute = mapData.BasePointers[i];5438 if (isDeclareTargetLink(mapOp.getVarPtrPtr() ? mapOp.getVarPtrPtr()5439 : mapOp.getVarPtr())) {5440 builder.SetCurrentDebugLocation(insn->getDebugLoc());5441 substitute = builder.CreateLoad(5442 mapData.BasePointers[i]->getType(), mapData.BasePointers[i]);5443 cast<llvm::LoadInst>(substitute)->moveBefore(insn->getIterator());5444 }5445 user->replaceUsesOfWith(mapData.OriginalValue[i], substitute);5446 }5447 }5448 }5449 }5450 }5451}5452 5453// The createDeviceArgumentAccessor function generates5454// instructions for retrieving (acessing) kernel5455// arguments inside of the device kernel for use by5456// the kernel. This enables different semantics such as5457// the creation of temporary copies of data allowing5458// semantics like read-only/no host write back kernel5459// arguments.5460//5461// This currently implements a very light version of Clang's5462// EmitParmDecl's handling of direct argument handling as well5463// as a portion of the argument access generation based on5464// capture types found at the end of emitOutlinedFunctionPrologue5465// in Clang. The indirect path handling of EmitParmDecl's may be5466// required for future work, but a direct 1-to-1 copy doesn't seem5467// possible as the logic is rather scattered throughout Clang's5468// lowering and perhaps we wish to deviate slightly.5469//5470// \param mapData - A container containing vectors of information5471// corresponding to the input argument, which should have a5472// corresponding entry in the MapInfoData containers5473// OrigialValue's.5474// \param arg - This is the generated kernel function argument that5475// corresponds to the passed in input argument. We generated different5476// accesses of this Argument, based on capture type and other Input5477// related information.5478// \param input - This is the host side value that will be passed to5479// the kernel i.e. the kernel input, we rewrite all uses of this within5480// the kernel (as we generate the kernel body based on the target's region5481// which maintians references to the original input) to the retVal argument5482// apon exit of this function inside of the OMPIRBuilder. This interlinks5483// the kernel argument to future uses of it in the function providing5484// appropriate "glue" instructions inbetween.5485// \param retVal - This is the value that all uses of input inside of the5486// kernel will be re-written to, the goal of this function is to generate5487// an appropriate location for the kernel argument to be accessed from,5488// e.g. ByRef will result in a temporary allocation location and then5489// a store of the kernel argument into this allocated memory which5490// will then be loaded from, ByCopy will use the allocated memory5491// directly.5492static llvm::IRBuilderBase::InsertPoint5493createDeviceArgumentAccessor(MapInfoData &mapData, llvm::Argument &arg,5494 llvm::Value *input, llvm::Value *&retVal,5495 llvm::IRBuilderBase &builder,5496 llvm::OpenMPIRBuilder &ompBuilder,5497 LLVM::ModuleTranslation &moduleTranslation,5498 llvm::IRBuilderBase::InsertPoint allocaIP,5499 llvm::IRBuilderBase::InsertPoint codeGenIP) {5500 assert(ompBuilder.Config.isTargetDevice() &&5501 "function only supported for target device codegen");5502 builder.restoreIP(allocaIP);5503 5504 omp::VariableCaptureKind capture = omp::VariableCaptureKind::ByRef;5505 LLVM::TypeToLLVMIRTranslator typeToLLVMIRTranslator(5506 ompBuilder.M.getContext());5507 unsigned alignmentValue = 0;5508 // Find the associated MapInfoData entry for the current input5509 for (size_t i = 0; i < mapData.MapClause.size(); ++i)5510 if (mapData.OriginalValue[i] == input) {5511 auto mapOp = cast<omp::MapInfoOp>(mapData.MapClause[i]);5512 capture = mapOp.getMapCaptureType();5513 // Get information of alignment of mapped object5514 alignmentValue = typeToLLVMIRTranslator.getPreferredAlignment(5515 mapOp.getVarType(), ompBuilder.M.getDataLayout());5516 break;5517 }5518 5519 unsigned int allocaAS = ompBuilder.M.getDataLayout().getAllocaAddrSpace();5520 unsigned int defaultAS =5521 ompBuilder.M.getDataLayout().getProgramAddressSpace();5522 5523 // Create the alloca for the argument the current point.5524 llvm::Value *v = builder.CreateAlloca(arg.getType(), allocaAS);5525 5526 if (allocaAS != defaultAS && arg.getType()->isPointerTy())5527 v = builder.CreateAddrSpaceCast(v, builder.getPtrTy(defaultAS));5528 5529 builder.CreateStore(&arg, v);5530 5531 builder.restoreIP(codeGenIP);5532 5533 switch (capture) {5534 case omp::VariableCaptureKind::ByCopy: {5535 retVal = v;5536 break;5537 }5538 case omp::VariableCaptureKind::ByRef: {5539 llvm::LoadInst *loadInst = builder.CreateAlignedLoad(5540 v->getType(), v,5541 ompBuilder.M.getDataLayout().getPrefTypeAlign(v->getType()));5542 // CreateAlignedLoad function creates similar LLVM IR:5543 // %res = load ptr, ptr %input, align 85544 // This LLVM IR does not contain information about alignment5545 // of the loaded value. We need to add !align metadata to unblock5546 // optimizer. The existence of the !align metadata on the instruction5547 // tells the optimizer that the value loaded is known to be aligned to5548 // a boundary specified by the integer value in the metadata node.5549 // Example:5550 // %res = load ptr, ptr %input, align 8, !align !align_md_node5551 // ^ ^5552 // | |5553 // alignment of %input address |5554 // |5555 // alignment of %res object5556 if (v->getType()->isPointerTy() && alignmentValue) {5557 llvm::MDBuilder MDB(builder.getContext());5558 loadInst->setMetadata(5559 llvm::LLVMContext::MD_align,5560 llvm::MDNode::get(builder.getContext(),5561 MDB.createConstant(llvm::ConstantInt::get(5562 llvm::Type::getInt64Ty(builder.getContext()),5563 alignmentValue))));5564 }5565 retVal = loadInst;5566 5567 break;5568 }5569 case omp::VariableCaptureKind::This:5570 case omp::VariableCaptureKind::VLAType:5571 // TODO: Consider returning error to use standard reporting for5572 // unimplemented features.5573 assert(false && "Currently unsupported capture kind");5574 break;5575 }5576 5577 return builder.saveIP();5578}5579 5580/// Follow uses of `host_eval`-defined block arguments of the given `omp.target`5581/// operation and populate output variables with their corresponding host value5582/// (i.e. operand evaluated outside of the target region), based on their uses5583/// inside of the target region.5584///5585/// Loop bounds and steps are only optionally populated, if output vectors are5586/// provided.5587static void5588extractHostEvalClauses(omp::TargetOp targetOp, Value &numThreads,5589 Value &numTeamsLower, Value &numTeamsUpper,5590 Value &threadLimit,5591 llvm::SmallVectorImpl<Value> *lowerBounds = nullptr,5592 llvm::SmallVectorImpl<Value> *upperBounds = nullptr,5593 llvm::SmallVectorImpl<Value> *steps = nullptr) {5594 auto blockArgIface = llvm::cast<omp::BlockArgOpenMPOpInterface>(*targetOp);5595 for (auto item : llvm::zip_equal(targetOp.getHostEvalVars(),5596 blockArgIface.getHostEvalBlockArgs())) {5597 Value hostEvalVar = std::get<0>(item), blockArg = std::get<1>(item);5598 5599 for (Operation *user : blockArg.getUsers()) {5600 llvm::TypeSwitch<Operation *>(user)5601 .Case([&](omp::TeamsOp teamsOp) {5602 if (teamsOp.getNumTeamsLower() == blockArg)5603 numTeamsLower = hostEvalVar;5604 else if (teamsOp.getNumTeamsUpper() == blockArg)5605 numTeamsUpper = hostEvalVar;5606 else if (teamsOp.getThreadLimit() == blockArg)5607 threadLimit = hostEvalVar;5608 else5609 llvm_unreachable("unsupported host_eval use");5610 })5611 .Case([&](omp::ParallelOp parallelOp) {5612 if (parallelOp.getNumThreads() == blockArg)5613 numThreads = hostEvalVar;5614 else5615 llvm_unreachable("unsupported host_eval use");5616 })5617 .Case([&](omp::LoopNestOp loopOp) {5618 auto processBounds =5619 [&](OperandRange opBounds,5620 llvm::SmallVectorImpl<Value> *outBounds) -> bool {5621 bool found = false;5622 for (auto [i, lb] : llvm::enumerate(opBounds)) {5623 if (lb == blockArg) {5624 found = true;5625 if (outBounds)5626 (*outBounds)[i] = hostEvalVar;5627 }5628 }5629 return found;5630 };5631 bool found =5632 processBounds(loopOp.getLoopLowerBounds(), lowerBounds);5633 found = processBounds(loopOp.getLoopUpperBounds(), upperBounds) ||5634 found;5635 found = processBounds(loopOp.getLoopSteps(), steps) || found;5636 (void)found;5637 assert(found && "unsupported host_eval use");5638 })5639 .DefaultUnreachable("unsupported host_eval use");5640 }5641 }5642}5643 5644/// If \p op is of the given type parameter, return it casted to that type.5645/// Otherwise, if its immediate parent operation (or some other higher-level5646/// parent, if \p immediateParent is false) is of that type, return that parent5647/// casted to the given type.5648///5649/// If \p op is \c null or neither it or its parent(s) are of the specified5650/// type, return a \c null operation.5651template <typename OpTy>5652static OpTy castOrGetParentOfType(Operation *op, bool immediateParent = false) {5653 if (!op)5654 return OpTy();5655 5656 if (OpTy casted = dyn_cast<OpTy>(op))5657 return casted;5658 5659 if (immediateParent)5660 return dyn_cast_if_present<OpTy>(op->getParentOp());5661 5662 return op->getParentOfType<OpTy>();5663}5664 5665/// If the given \p value is defined by an \c llvm.mlir.constant operation and5666/// it is of an integer type, return its value.5667static std::optional<int64_t> extractConstInteger(Value value) {5668 if (!value)5669 return std::nullopt;5670 5671 if (auto constOp = value.getDefiningOp<LLVM::ConstantOp>())5672 if (auto constAttr = dyn_cast<IntegerAttr>(constOp.getValue()))5673 return constAttr.getInt();5674 5675 return std::nullopt;5676}5677 5678static uint64_t getTypeByteSize(mlir::Type type, const DataLayout &dl) {5679 uint64_t sizeInBits = dl.getTypeSizeInBits(type);5680 uint64_t sizeInBytes = sizeInBits / 8;5681 return sizeInBytes;5682}5683 5684template <typename OpTy>5685static uint64_t getReductionDataSize(OpTy &op) {5686 if (op.getNumReductionVars() > 0) {5687 SmallVector<omp::DeclareReductionOp> reductions;5688 collectReductionDecls(op, reductions);5689 5690 llvm::SmallVector<mlir::Type> members;5691 members.reserve(reductions.size());5692 for (omp::DeclareReductionOp &red : reductions)5693 members.push_back(red.getType());5694 Operation *opp = op.getOperation();5695 auto structType = mlir::LLVM::LLVMStructType::getLiteral(5696 opp->getContext(), members, /*isPacked=*/false);5697 DataLayout dl = DataLayout(opp->getParentOfType<ModuleOp>());5698 return getTypeByteSize(structType, dl);5699 }5700 return 0;5701}5702 5703/// Populate default `MinTeams`, `MaxTeams` and `MaxThreads` to their default5704/// values as stated by the corresponding clauses, if constant.5705///5706/// These default values must be set before the creation of the outlined LLVM5707/// function for the target region, so that they can be used to initialize the5708/// corresponding global `ConfigurationEnvironmentTy` structure.5709static void5710initTargetDefaultAttrs(omp::TargetOp targetOp, Operation *capturedOp,5711 llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs &attrs,5712 bool isTargetDevice, bool isGPU) {5713 // TODO: Handle constant 'if' clauses.5714 5715 Value numThreads, numTeamsLower, numTeamsUpper, threadLimit;5716 if (!isTargetDevice) {5717 extractHostEvalClauses(targetOp, numThreads, numTeamsLower, numTeamsUpper,5718 threadLimit);5719 } else {5720 // In the target device, values for these clauses are not passed as5721 // host_eval, but instead evaluated prior to entry to the region. This5722 // ensures values are mapped and available inside of the target region.5723 if (auto teamsOp = castOrGetParentOfType<omp::TeamsOp>(capturedOp)) {5724 numTeamsLower = teamsOp.getNumTeamsLower();5725 numTeamsUpper = teamsOp.getNumTeamsUpper();5726 threadLimit = teamsOp.getThreadLimit();5727 }5728 5729 if (auto parallelOp = castOrGetParentOfType<omp::ParallelOp>(capturedOp))5730 numThreads = parallelOp.getNumThreads();5731 }5732 5733 // Handle clauses impacting the number of teams.5734 5735 int32_t minTeamsVal = 1, maxTeamsVal = -1;5736 if (castOrGetParentOfType<omp::TeamsOp>(capturedOp)) {5737 // TODO: Use `hostNumTeamsLower` to initialize `minTeamsVal`. For now,5738 // match clang and set min and max to the same value.5739 if (numTeamsUpper) {5740 if (auto val = extractConstInteger(numTeamsUpper))5741 minTeamsVal = maxTeamsVal = *val;5742 } else {5743 minTeamsVal = maxTeamsVal = 0;5744 }5745 } else if (castOrGetParentOfType<omp::ParallelOp>(capturedOp,5746 /*immediateParent=*/true) ||5747 castOrGetParentOfType<omp::SimdOp>(capturedOp,5748 /*immediateParent=*/true)) {5749 minTeamsVal = maxTeamsVal = 1;5750 } else {5751 minTeamsVal = maxTeamsVal = -1;5752 }5753 5754 // Handle clauses impacting the number of threads.5755 5756 auto setMaxValueFromClause = [](Value clauseValue, int32_t &result) {5757 if (!clauseValue)5758 return;5759 5760 if (auto val = extractConstInteger(clauseValue))5761 result = *val;5762 5763 // Found an applicable clause, so it's not undefined. Mark as unknown5764 // because it's not constant.5765 if (result < 0)5766 result = 0;5767 };5768 5769 // Extract 'thread_limit' clause from 'target' and 'teams' directives.5770 int32_t targetThreadLimitVal = -1, teamsThreadLimitVal = -1;5771 setMaxValueFromClause(targetOp.getThreadLimit(), targetThreadLimitVal);5772 setMaxValueFromClause(threadLimit, teamsThreadLimitVal);5773 5774 // Extract 'max_threads' clause from 'parallel' or set to 1 if it's SIMD.5775 int32_t maxThreadsVal = -1;5776 if (castOrGetParentOfType<omp::ParallelOp>(capturedOp))5777 setMaxValueFromClause(numThreads, maxThreadsVal);5778 else if (castOrGetParentOfType<omp::SimdOp>(capturedOp,5779 /*immediateParent=*/true))5780 maxThreadsVal = 1;5781 5782 // For max values, < 0 means unset, == 0 means set but unknown. Select the5783 // minimum value between 'max_threads' and 'thread_limit' clauses that were5784 // set.5785 int32_t combinedMaxThreadsVal = targetThreadLimitVal;5786 if (combinedMaxThreadsVal < 0 ||5787 (teamsThreadLimitVal >= 0 && teamsThreadLimitVal < combinedMaxThreadsVal))5788 combinedMaxThreadsVal = teamsThreadLimitVal;5789 5790 if (combinedMaxThreadsVal < 0 ||5791 (maxThreadsVal >= 0 && maxThreadsVal < combinedMaxThreadsVal))5792 combinedMaxThreadsVal = maxThreadsVal;5793 5794 int32_t reductionDataSize = 0;5795 if (isGPU && capturedOp) {5796 if (auto teamsOp = castOrGetParentOfType<omp::TeamsOp>(capturedOp))5797 reductionDataSize = getReductionDataSize(teamsOp);5798 }5799 5800 // Update kernel bounds structure for the `OpenMPIRBuilder` to use.5801 omp::TargetRegionFlags kernelFlags = targetOp.getKernelExecFlags(capturedOp);5802 assert(5803 omp::bitEnumContainsAny(kernelFlags, omp::TargetRegionFlags::generic |5804 omp::TargetRegionFlags::spmd) &&5805 "invalid kernel flags");5806 attrs.ExecFlags =5807 omp::bitEnumContainsAny(kernelFlags, omp::TargetRegionFlags::generic)5808 ? omp::bitEnumContainsAny(kernelFlags, omp::TargetRegionFlags::spmd)5809 ? llvm::omp::OMP_TGT_EXEC_MODE_GENERIC_SPMD5810 : llvm::omp::OMP_TGT_EXEC_MODE_GENERIC5811 : llvm::omp::OMP_TGT_EXEC_MODE_SPMD;5812 if (omp::bitEnumContainsAll(kernelFlags,5813 omp::TargetRegionFlags::spmd |5814 omp::TargetRegionFlags::no_loop) &&5815 !omp::bitEnumContainsAny(kernelFlags, omp::TargetRegionFlags::generic))5816 attrs.ExecFlags = llvm::omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP;5817 5818 attrs.MinTeams = minTeamsVal;5819 attrs.MaxTeams.front() = maxTeamsVal;5820 attrs.MinThreads = 1;5821 attrs.MaxThreads.front() = combinedMaxThreadsVal;5822 attrs.ReductionDataSize = reductionDataSize;5823 // TODO: Allow modified buffer length similar to5824 // fopenmp-cuda-teams-reduction-recs-num flag in clang.5825 if (attrs.ReductionDataSize != 0)5826 attrs.ReductionBufferLength = 1024;5827}5828 5829/// Gather LLVM runtime values for all clauses evaluated in the host that are5830/// passed to the kernel invocation.5831///5832/// This function must be called only when compiling for the host. Also, it will5833/// only provide correct results if it's called after the body of \c targetOp5834/// has been fully generated.5835static void5836initTargetRuntimeAttrs(llvm::IRBuilderBase &builder,5837 LLVM::ModuleTranslation &moduleTranslation,5838 omp::TargetOp targetOp, Operation *capturedOp,5839 llvm::OpenMPIRBuilder::TargetKernelRuntimeAttrs &attrs) {5840 omp::LoopNestOp loopOp = castOrGetParentOfType<omp::LoopNestOp>(capturedOp);5841 unsigned numLoops = loopOp ? loopOp.getNumLoops() : 0;5842 5843 Value numThreads, numTeamsLower, numTeamsUpper, teamsThreadLimit;5844 llvm::SmallVector<Value> lowerBounds(numLoops), upperBounds(numLoops),5845 steps(numLoops);5846 extractHostEvalClauses(targetOp, numThreads, numTeamsLower, numTeamsUpper,5847 teamsThreadLimit, &lowerBounds, &upperBounds, &steps);5848 5849 // TODO: Handle constant 'if' clauses.5850 if (Value targetThreadLimit = targetOp.getThreadLimit())5851 attrs.TargetThreadLimit.front() =5852 moduleTranslation.lookupValue(targetThreadLimit);5853 5854 if (numTeamsLower)5855 attrs.MinTeams = moduleTranslation.lookupValue(numTeamsLower);5856 5857 if (numTeamsUpper)5858 attrs.MaxTeams.front() = moduleTranslation.lookupValue(numTeamsUpper);5859 5860 if (teamsThreadLimit)5861 attrs.TeamsThreadLimit.front() =5862 moduleTranslation.lookupValue(teamsThreadLimit);5863 5864 if (numThreads)5865 attrs.MaxThreads = moduleTranslation.lookupValue(numThreads);5866 5867 if (omp::bitEnumContainsAny(targetOp.getKernelExecFlags(capturedOp),5868 omp::TargetRegionFlags::trip_count)) {5869 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();5870 attrs.LoopTripCount = nullptr;5871 5872 // To calculate the trip count, we multiply together the trip counts of5873 // every collapsed canonical loop. We don't need to create the loop nests5874 // here, since we're only interested in the trip count.5875 for (auto [loopLower, loopUpper, loopStep] :5876 llvm::zip_equal(lowerBounds, upperBounds, steps)) {5877 llvm::Value *lowerBound = moduleTranslation.lookupValue(loopLower);5878 llvm::Value *upperBound = moduleTranslation.lookupValue(loopUpper);5879 llvm::Value *step = moduleTranslation.lookupValue(loopStep);5880 5881 llvm::OpenMPIRBuilder::LocationDescription loc(builder);5882 llvm::Value *tripCount = ompBuilder->calculateCanonicalLoopTripCount(5883 loc, lowerBound, upperBound, step, /*IsSigned=*/true,5884 loopOp.getLoopInclusive());5885 5886 if (!attrs.LoopTripCount) {5887 attrs.LoopTripCount = tripCount;5888 continue;5889 }5890 5891 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.5892 attrs.LoopTripCount = builder.CreateMul(attrs.LoopTripCount, tripCount,5893 {}, /*HasNUW=*/true);5894 }5895 }5896}5897 5898static LogicalResult5899convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder,5900 LLVM::ModuleTranslation &moduleTranslation) {5901 auto targetOp = cast<omp::TargetOp>(opInst);5902 // The current debug location already has the DISubprogram for the outlined5903 // function that will be created for the target op. We save it here so that5904 // we can set it on the outlined function.5905 llvm::DebugLoc outlinedFnLoc = builder.getCurrentDebugLocation();5906 if (failed(checkImplementationStatus(opInst)))5907 return failure();5908 5909 // During the handling of target op, we will generate instructions in the5910 // parent function like call to the oulined function or branch to a new5911 // BasicBlock. We set the debug location here to parent function so that those5912 // get the correct debug locations. For outlined functions, the normal MLIR op5913 // conversion will automatically pick the correct location.5914 llvm::BasicBlock *parentBB = builder.GetInsertBlock();5915 assert(parentBB && "No insert block is set for the builder");5916 llvm::Function *parentLLVMFn = parentBB->getParent();5917 assert(parentLLVMFn && "Parent Function must be valid");5918 if (llvm::DISubprogram *SP = parentLLVMFn->getSubprogram())5919 builder.SetCurrentDebugLocation(llvm::DILocation::get(5920 parentLLVMFn->getContext(), outlinedFnLoc.getLine(),5921 outlinedFnLoc.getCol(), SP, outlinedFnLoc.getInlinedAt()));5922 5923 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();5924 bool isTargetDevice = ompBuilder->Config.isTargetDevice();5925 bool isGPU = ompBuilder->Config.isGPU();5926 5927 auto parentFn = opInst.getParentOfType<LLVM::LLVMFuncOp>();5928 auto argIface = cast<omp::BlockArgOpenMPOpInterface>(opInst);5929 auto &targetRegion = targetOp.getRegion();5930 // Holds the private vars that have been mapped along with the block5931 // argument that corresponds to the MapInfoOp corresponding to the private5932 // var in question. So, for instance:5933 //5934 // %10 = omp.map.info var_ptr(%6#0 : !fir.ref<!fir.box<!fir.heap<i32>>>, ..)5935 // omp.target map_entries(%10 -> %arg0) private(@box.privatizer %6#0-> %arg1)5936 //5937 // Then, %10 has been created so that the descriptor can be used by the5938 // privatizer @box.privatizer on the device side. Here we'd record {%6#0,5939 // %arg0} in the mappedPrivateVars map.5940 llvm::DenseMap<Value, Value> mappedPrivateVars;5941 DataLayout dl = DataLayout(opInst.getParentOfType<ModuleOp>());5942 SmallVector<Value> mapVars = targetOp.getMapVars();5943 SmallVector<Value> hdaVars = targetOp.getHasDeviceAddrVars();5944 ArrayRef<BlockArgument> mapBlockArgs = argIface.getMapBlockArgs();5945 ArrayRef<BlockArgument> hdaBlockArgs = argIface.getHasDeviceAddrBlockArgs();5946 llvm::Function *llvmOutlinedFn = nullptr;5947 TargetDirectiveEnumTy targetDirective =5948 getTargetDirectiveEnumTyFromOp(&opInst);5949 5950 // TODO: It can also be false if a compile-time constant `false` IF clause is5951 // specified.5952 bool isOffloadEntry =5953 isTargetDevice || !ompBuilder->Config.TargetTriples.empty();5954 5955 // For some private variables, the MapsForPrivatizedVariablesPass5956 // creates MapInfoOp instances. Go through the private variables and5957 // the mapped variables so that during codegeneration we are able5958 // to quickly look up the corresponding map variable, if any for each5959 // private variable.5960 if (!targetOp.getPrivateVars().empty() && !targetOp.getMapVars().empty()) {5961 OperandRange privateVars = targetOp.getPrivateVars();5962 std::optional<ArrayAttr> privateSyms = targetOp.getPrivateSyms();5963 std::optional<DenseI64ArrayAttr> privateMapIndices =5964 targetOp.getPrivateMapsAttr();5965 5966 for (auto [privVarIdx, privVarSymPair] :5967 llvm::enumerate(llvm::zip_equal(privateVars, *privateSyms))) {5968 auto privVar = std::get<0>(privVarSymPair);5969 auto privSym = std::get<1>(privVarSymPair);5970 5971 SymbolRefAttr privatizerName = llvm::cast<SymbolRefAttr>(privSym);5972 omp::PrivateClauseOp privatizer =5973 findPrivatizer(targetOp, privatizerName);5974 5975 if (!privatizer.needsMap())5976 continue;5977 5978 mlir::Value mappedValue =5979 targetOp.getMappedValueForPrivateVar(privVarIdx);5980 assert(mappedValue && "Expected to find mapped value for a privatized "5981 "variable that needs mapping");5982 5983 // The MapInfoOp defining the map var isn't really needed later.5984 // So, we don't store it in any datastructure. Instead, we just5985 // do some sanity checks on it right now.5986 auto mapInfoOp = mappedValue.getDefiningOp<omp::MapInfoOp>();5987 [[maybe_unused]] Type varType = mapInfoOp.getVarType();5988 5989 // Check #1: Check that the type of the private variable matches5990 // the type of the variable being mapped.5991 if (!isa<LLVM::LLVMPointerType>(privVar.getType()))5992 assert(5993 varType == privVar.getType() &&5994 "Type of private var doesn't match the type of the mapped value");5995 5996 // Ok, only 1 sanity check for now.5997 // Record the block argument corresponding to this mapvar.5998 mappedPrivateVars.insert(5999 {privVar,6000 targetRegion.getArgument(argIface.getMapBlockArgsStart() +6001 (*privateMapIndices)[privVarIdx])});6002 }6003 }6004 6005 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;6006 auto bodyCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP)6007 -> llvm::OpenMPIRBuilder::InsertPointOrErrorTy {6008 llvm::IRBuilderBase::InsertPointGuard guard(builder);6009 builder.SetCurrentDebugLocation(llvm::DebugLoc());6010 // Forward target-cpu and target-features function attributes from the6011 // original function to the new outlined function.6012 llvm::Function *llvmParentFn =6013 moduleTranslation.lookupFunction(parentFn.getName());6014 llvmOutlinedFn = codeGenIP.getBlock()->getParent();6015 assert(llvmParentFn && llvmOutlinedFn &&6016 "Both parent and outlined functions must exist at this point");6017 6018 if (outlinedFnLoc && llvmParentFn->getSubprogram())6019 llvmOutlinedFn->setSubprogram(outlinedFnLoc->getScope()->getSubprogram());6020 6021 if (auto attr = llvmParentFn->getFnAttribute("target-cpu");6022 attr.isStringAttribute())6023 llvmOutlinedFn->addFnAttr(attr);6024 6025 if (auto attr = llvmParentFn->getFnAttribute("target-features");6026 attr.isStringAttribute())6027 llvmOutlinedFn->addFnAttr(attr);6028 6029 for (auto [arg, mapOp] : llvm::zip_equal(mapBlockArgs, mapVars)) {6030 auto mapInfoOp = cast<omp::MapInfoOp>(mapOp.getDefiningOp());6031 llvm::Value *mapOpValue =6032 moduleTranslation.lookupValue(mapInfoOp.getVarPtr());6033 moduleTranslation.mapValue(arg, mapOpValue);6034 }6035 for (auto [arg, mapOp] : llvm::zip_equal(hdaBlockArgs, hdaVars)) {6036 auto mapInfoOp = cast<omp::MapInfoOp>(mapOp.getDefiningOp());6037 llvm::Value *mapOpValue =6038 moduleTranslation.lookupValue(mapInfoOp.getVarPtr());6039 moduleTranslation.mapValue(arg, mapOpValue);6040 }6041 6042 // Do privatization after moduleTranslation has already recorded6043 // mapped values.6044 PrivateVarsInfo privateVarsInfo(targetOp);6045 6046 llvm::Expected<llvm::BasicBlock *> afterAllocas =6047 allocatePrivateVars(builder, moduleTranslation, privateVarsInfo,6048 allocaIP, &mappedPrivateVars);6049 6050 if (failed(handleError(afterAllocas, *targetOp)))6051 return llvm::make_error<PreviouslyReportedError>();6052 6053 builder.restoreIP(codeGenIP);6054 if (handleError(initPrivateVars(builder, moduleTranslation, privateVarsInfo,6055 &mappedPrivateVars),6056 *targetOp)6057 .failed())6058 return llvm::make_error<PreviouslyReportedError>();6059 6060 if (failed(copyFirstPrivateVars(6061 targetOp, builder, moduleTranslation, privateVarsInfo.mlirVars,6062 privateVarsInfo.llvmVars, privateVarsInfo.privatizers,6063 targetOp.getPrivateNeedsBarrier(), &mappedPrivateVars)))6064 return llvm::make_error<PreviouslyReportedError>();6065 6066 SmallVector<Region *> privateCleanupRegions;6067 llvm::transform(privateVarsInfo.privatizers,6068 std::back_inserter(privateCleanupRegions),6069 [](omp::PrivateClauseOp privatizer) {6070 return &privatizer.getDeallocRegion();6071 });6072 6073 llvm::Expected<llvm::BasicBlock *> exitBlock = convertOmpOpRegions(6074 targetRegion, "omp.target", builder, moduleTranslation);6075 6076 if (!exitBlock)6077 return exitBlock.takeError();6078 6079 builder.SetInsertPoint(*exitBlock);6080 if (!privateCleanupRegions.empty()) {6081 if (failed(inlineOmpRegionCleanup(6082 privateCleanupRegions, privateVarsInfo.llvmVars,6083 moduleTranslation, builder, "omp.targetop.private.cleanup",6084 /*shouldLoadCleanupRegionArg=*/false))) {6085 return llvm::createStringError(6086 "failed to inline `dealloc` region of `omp.private` "6087 "op in the target region");6088 }6089 return builder.saveIP();6090 }6091 6092 return InsertPointTy(exitBlock.get(), exitBlock.get()->end());6093 };6094 6095 StringRef parentName = parentFn.getName();6096 6097 llvm::TargetRegionEntryInfo entryInfo;6098 6099 getTargetEntryUniqueInfo(entryInfo, targetOp, parentName);6100 6101 MapInfoData mapData;6102 collectMapDataFromMapOperands(mapData, mapVars, moduleTranslation, dl,6103 builder, /*useDevPtrOperands=*/{},6104 /*useDevAddrOperands=*/{}, hdaVars);6105 6106 MapInfosTy combinedInfos;6107 auto genMapInfoCB =6108 [&](llvm::OpenMPIRBuilder::InsertPointTy codeGenIP) -> MapInfosTy & {6109 builder.restoreIP(codeGenIP);6110 genMapInfos(builder, moduleTranslation, dl, combinedInfos, mapData,6111 targetDirective);6112 return combinedInfos;6113 };6114 6115 auto argAccessorCB = [&](llvm::Argument &arg, llvm::Value *input,6116 llvm::Value *&retVal, InsertPointTy allocaIP,6117 InsertPointTy codeGenIP)6118 -> llvm::OpenMPIRBuilder::InsertPointOrErrorTy {6119 llvm::IRBuilderBase::InsertPointGuard guard(builder);6120 builder.SetCurrentDebugLocation(llvm::DebugLoc());6121 // We just return the unaltered argument for the host function6122 // for now, some alterations may be required in the future to6123 // keep host fallback functions working identically to the device6124 // version (e.g. pass ByCopy values should be treated as such on6125 // host and device, currently not always the case)6126 if (!isTargetDevice) {6127 retVal = cast<llvm::Value>(&arg);6128 return codeGenIP;6129 }6130 6131 return createDeviceArgumentAccessor(mapData, arg, input, retVal, builder,6132 *ompBuilder, moduleTranslation,6133 allocaIP, codeGenIP);6134 };6135 6136 llvm::OpenMPIRBuilder::TargetKernelRuntimeAttrs runtimeAttrs;6137 llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs defaultAttrs;6138 Operation *targetCapturedOp = targetOp.getInnermostCapturedOmpOp();6139 initTargetDefaultAttrs(targetOp, targetCapturedOp, defaultAttrs,6140 isTargetDevice, isGPU);6141 6142 // Collect host-evaluated values needed to properly launch the kernel from the6143 // host.6144 if (!isTargetDevice)6145 initTargetRuntimeAttrs(builder, moduleTranslation, targetOp,6146 targetCapturedOp, runtimeAttrs);6147 6148 // Pass host-evaluated values as parameters to the kernel / host fallback,6149 // except if they are constants. In any case, map the MLIR block argument to6150 // the corresponding LLVM values.6151 llvm::SmallVector<llvm::Value *, 4> kernelInput;6152 SmallVector<Value> hostEvalVars = targetOp.getHostEvalVars();6153 ArrayRef<BlockArgument> hostEvalBlockArgs = argIface.getHostEvalBlockArgs();6154 for (auto [arg, var] : llvm::zip_equal(hostEvalBlockArgs, hostEvalVars)) {6155 llvm::Value *value = moduleTranslation.lookupValue(var);6156 moduleTranslation.mapValue(arg, value);6157 6158 if (!llvm::isa<llvm::Constant>(value))6159 kernelInput.push_back(value);6160 }6161 6162 for (size_t i = 0, e = mapData.OriginalValue.size(); i != e; ++i) {6163 // declare target arguments are not passed to kernels as arguments6164 // TODO: We currently do not handle cases where a member is explicitly6165 // passed in as an argument, this will likley need to be handled in6166 // the near future, rather than using IsAMember, it may be better to6167 // test if the relevant BlockArg is used within the target region and6168 // then use that as a basis for exclusion in the kernel inputs.6169 if (!mapData.IsDeclareTarget[i] && !mapData.IsAMember[i])6170 kernelInput.push_back(mapData.OriginalValue[i]);6171 }6172 6173 SmallVector<llvm::OpenMPIRBuilder::DependData> dds;6174 buildDependData(targetOp.getDependKinds(), targetOp.getDependVars(),6175 moduleTranslation, dds);6176 6177 llvm::OpenMPIRBuilder::InsertPointTy allocaIP =6178 findAllocaInsertPoint(builder, moduleTranslation);6179 llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);6180 6181 llvm::OpenMPIRBuilder::TargetDataInfo info(6182 /*RequiresDevicePointerInfo=*/false,6183 /*SeparateBeginEndCalls=*/true);6184 6185 auto customMapperCB =6186 [&](unsigned int i) -> llvm::Expected<llvm::Function *> {6187 if (!combinedInfos.Mappers[i])6188 return nullptr;6189 info.HasMapper = true;6190 return getOrCreateUserDefinedMapperFunc(combinedInfos.Mappers[i], builder,6191 moduleTranslation, targetDirective);6192 };6193 6194 llvm::Value *ifCond = nullptr;6195 if (Value targetIfCond = targetOp.getIfExpr())6196 ifCond = moduleTranslation.lookupValue(targetIfCond);6197 6198 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =6199 moduleTranslation.getOpenMPBuilder()->createTarget(6200 ompLoc, isOffloadEntry, allocaIP, builder.saveIP(), info, entryInfo,6201 defaultAttrs, runtimeAttrs, ifCond, kernelInput, genMapInfoCB, bodyCB,6202 argAccessorCB, customMapperCB, dds, targetOp.getNowait());6203 6204 if (failed(handleError(afterIP, opInst)))6205 return failure();6206 6207 builder.restoreIP(*afterIP);6208 6209 // Remap access operations to declare target reference pointers for the6210 // device, essentially generating extra loadop's as necessary6211 if (moduleTranslation.getOpenMPBuilder()->Config.isTargetDevice())6212 handleDeclareTargetMapVar(mapData, moduleTranslation, builder,6213 llvmOutlinedFn);6214 6215 return success();6216}6217 6218static LogicalResult6219convertDeclareTargetAttr(Operation *op, mlir::omp::DeclareTargetAttr attribute,6220 LLVM::ModuleTranslation &moduleTranslation) {6221 // Amend omp.declare_target by deleting the IR of the outlined functions6222 // created for target regions. They cannot be filtered out from MLIR earlier6223 // because the omp.target operation inside must be translated to LLVM, but6224 // the wrapper functions themselves must not remain at the end of the6225 // process. We know that functions where omp.declare_target does not match6226 // omp.is_target_device at this stage can only be wrapper functions because6227 // those that aren't are removed earlier as an MLIR transformation pass.6228 if (FunctionOpInterface funcOp = dyn_cast<FunctionOpInterface>(op)) {6229 if (auto offloadMod = dyn_cast<omp::OffloadModuleInterface>(6230 op->getParentOfType<ModuleOp>().getOperation())) {6231 if (!offloadMod.getIsTargetDevice())6232 return success();6233 6234 omp::DeclareTargetDeviceType declareType =6235 attribute.getDeviceType().getValue();6236 6237 if (declareType == omp::DeclareTargetDeviceType::host) {6238 llvm::Function *llvmFunc =6239 moduleTranslation.lookupFunction(funcOp.getName());6240 llvmFunc->dropAllReferences();6241 llvmFunc->eraseFromParent();6242 }6243 }6244 return success();6245 }6246 6247 if (LLVM::GlobalOp gOp = dyn_cast<LLVM::GlobalOp>(op)) {6248 llvm::Module *llvmModule = moduleTranslation.getLLVMModule();6249 if (auto *gVal = llvmModule->getNamedValue(gOp.getSymName())) {6250 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();6251 bool isDeclaration = gOp.isDeclaration();6252 bool isExternallyVisible =6253 gOp.getVisibility() != mlir::SymbolTable::Visibility::Private;6254 auto loc = op->getLoc()->findInstanceOf<FileLineColLoc>();6255 llvm::StringRef mangledName = gOp.getSymName();6256 auto captureClause =6257 convertToCaptureClauseKind(attribute.getCaptureClause().getValue());6258 auto deviceClause =6259 convertToDeviceClauseKind(attribute.getDeviceType().getValue());6260 // unused for MLIR at the moment, required in Clang for book6261 // keeping6262 std::vector<llvm::GlobalVariable *> generatedRefs;6263 6264 std::vector<llvm::Triple> targetTriple;6265 auto targetTripleAttr = dyn_cast_or_null<mlir::StringAttr>(6266 op->getParentOfType<mlir::ModuleOp>()->getAttr(6267 LLVM::LLVMDialect::getTargetTripleAttrName()));6268 if (targetTripleAttr)6269 targetTriple.emplace_back(targetTripleAttr.data());6270 6271 auto fileInfoCallBack = [&loc]() {6272 std::string filename = "";6273 std::uint64_t lineNo = 0;6274 6275 if (loc) {6276 filename = loc.getFilename().str();6277 lineNo = loc.getLine();6278 }6279 6280 return std::pair<std::string, std::uint64_t>(llvm::StringRef(filename),6281 lineNo);6282 };6283 6284 auto vfs = llvm::vfs::getRealFileSystem();6285 6286 ompBuilder->registerTargetGlobalVariable(6287 captureClause, deviceClause, isDeclaration, isExternallyVisible,6288 ompBuilder->getTargetEntryUniqueInfo(fileInfoCallBack, *vfs),6289 mangledName, generatedRefs, /*OpenMPSimd*/ false, targetTriple,6290 /*GlobalInitializer*/ nullptr, /*VariableLinkage*/ nullptr,6291 gVal->getType(), gVal);6292 6293 if (ompBuilder->Config.isTargetDevice() &&6294 (attribute.getCaptureClause().getValue() !=6295 mlir::omp::DeclareTargetCaptureClause::to ||6296 ompBuilder->Config.hasRequiresUnifiedSharedMemory())) {6297 ompBuilder->getAddrOfDeclareTargetVar(6298 captureClause, deviceClause, isDeclaration, isExternallyVisible,6299 ompBuilder->getTargetEntryUniqueInfo(fileInfoCallBack, *vfs),6300 mangledName, generatedRefs, /*OpenMPSimd*/ false, targetTriple,6301 gVal->getType(), /*GlobalInitializer*/ nullptr,6302 /*VariableLinkage*/ nullptr);6303 }6304 }6305 }6306 6307 return success();6308}6309 6310// Returns true if the operation is inside a TargetOp or6311// is part of a declare target function.6312static bool isTargetDeviceOp(Operation *op) {6313 // Assumes no reverse offloading6314 if (op->getParentOfType<omp::TargetOp>())6315 return true;6316 6317 // Certain operations return results, and whether utilised in host or6318 // target there is a chance an LLVM Dialect operation depends on it6319 // by taking it in as an operand, so we must always lower these in6320 // some manner or result in an ICE (whether they end up in a no-op6321 // or otherwise).6322 if (mlir::isa<omp::ThreadprivateOp>(op))6323 return true;6324 6325 if (mlir::isa<omp::TargetAllocMemOp>(op) ||6326 mlir::isa<omp::TargetFreeMemOp>(op))6327 return true;6328 6329 if (auto parentFn = op->getParentOfType<LLVM::LLVMFuncOp>())6330 if (auto declareTargetIface =6331 llvm::dyn_cast<mlir::omp::DeclareTargetInterface>(6332 parentFn.getOperation()))6333 if (declareTargetIface.isDeclareTarget() &&6334 declareTargetIface.getDeclareTargetDeviceType() !=6335 mlir::omp::DeclareTargetDeviceType::host)6336 return true;6337 6338 return false;6339}6340 6341static llvm::Function *getOmpTargetAlloc(llvm::IRBuilderBase &builder,6342 llvm::Module *llvmModule) {6343 llvm::Type *i64Ty = builder.getInt64Ty();6344 llvm::Type *i32Ty = builder.getInt32Ty();6345 llvm::Type *returnType = builder.getPtrTy(0);6346 llvm::FunctionType *fnType =6347 llvm::FunctionType::get(returnType, {i64Ty, i32Ty}, false);6348 llvm::Function *func = cast<llvm::Function>(6349 llvmModule->getOrInsertFunction("omp_target_alloc", fnType).getCallee());6350 return func;6351}6352 6353static LogicalResult6354convertTargetAllocMemOp(Operation &opInst, llvm::IRBuilderBase &builder,6355 LLVM::ModuleTranslation &moduleTranslation) {6356 auto allocMemOp = cast<omp::TargetAllocMemOp>(opInst);6357 if (!allocMemOp)6358 return failure();6359 6360 // Get "omp_target_alloc" function6361 llvm::Module *llvmModule = moduleTranslation.getLLVMModule();6362 llvm::Function *ompTargetAllocFunc = getOmpTargetAlloc(builder, llvmModule);6363 // Get the corresponding device value in llvm6364 mlir::Value deviceNum = allocMemOp.getDevice();6365 llvm::Value *llvmDeviceNum = moduleTranslation.lookupValue(deviceNum);6366 // Get the allocation size.6367 llvm::DataLayout dataLayout = llvmModule->getDataLayout();6368 mlir::Type heapTy = allocMemOp.getAllocatedType();6369 llvm::Type *llvmHeapTy = moduleTranslation.convertType(heapTy);6370 llvm::TypeSize typeSize = dataLayout.getTypeStoreSize(llvmHeapTy);6371 llvm::Value *allocSize = builder.getInt64(typeSize.getFixedValue());6372 for (auto typeParam : allocMemOp.getTypeparams())6373 allocSize =6374 builder.CreateMul(allocSize, moduleTranslation.lookupValue(typeParam));6375 // Create call to "omp_target_alloc" with the args as translated llvm values.6376 llvm::CallInst *call =6377 builder.CreateCall(ompTargetAllocFunc, {allocSize, llvmDeviceNum});6378 llvm::Value *resultI64 = builder.CreatePtrToInt(call, builder.getInt64Ty());6379 6380 // Map the result6381 moduleTranslation.mapValue(allocMemOp.getResult(), resultI64);6382 return success();6383}6384 6385static llvm::Function *getOmpTargetFree(llvm::IRBuilderBase &builder,6386 llvm::Module *llvmModule) {6387 llvm::Type *ptrTy = builder.getPtrTy(0);6388 llvm::Type *i32Ty = builder.getInt32Ty();6389 llvm::Type *voidTy = builder.getVoidTy();6390 llvm::FunctionType *fnType =6391 llvm::FunctionType::get(voidTy, {ptrTy, i32Ty}, false);6392 llvm::Function *func = dyn_cast<llvm::Function>(6393 llvmModule->getOrInsertFunction("omp_target_free", fnType).getCallee());6394 return func;6395}6396 6397static LogicalResult6398convertTargetFreeMemOp(Operation &opInst, llvm::IRBuilderBase &builder,6399 LLVM::ModuleTranslation &moduleTranslation) {6400 auto freeMemOp = cast<omp::TargetFreeMemOp>(opInst);6401 if (!freeMemOp)6402 return failure();6403 6404 // Get "omp_target_free" function6405 llvm::Module *llvmModule = moduleTranslation.getLLVMModule();6406 llvm::Function *ompTragetFreeFunc = getOmpTargetFree(builder, llvmModule);6407 // Get the corresponding device value in llvm6408 mlir::Value deviceNum = freeMemOp.getDevice();6409 llvm::Value *llvmDeviceNum = moduleTranslation.lookupValue(deviceNum);6410 // Get the corresponding heapref value in llvm6411 mlir::Value heapref = freeMemOp.getHeapref();6412 llvm::Value *llvmHeapref = moduleTranslation.lookupValue(heapref);6413 // Convert heapref int to ptr and call "omp_target_free"6414 llvm::Value *intToPtr =6415 builder.CreateIntToPtr(llvmHeapref, builder.getPtrTy(0));6416 builder.CreateCall(ompTragetFreeFunc, {intToPtr, llvmDeviceNum});6417 return success();6418}6419 6420/// Given an OpenMP MLIR operation, create the corresponding LLVM IR (including6421/// OpenMP runtime calls).6422static LogicalResult6423convertHostOrTargetOperation(Operation *op, llvm::IRBuilderBase &builder,6424 LLVM::ModuleTranslation &moduleTranslation) {6425 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();6426 6427 // For each loop, introduce one stack frame to hold loop information. Ensure6428 // this is only done for the outermost loop wrapper to prevent introducing6429 // multiple stack frames for a single loop. Initially set to null, the loop6430 // information structure is initialized during translation of the nested6431 // omp.loop_nest operation, making it available to translation of all loop6432 // wrappers after their body has been successfully translated.6433 bool isOutermostLoopWrapper =6434 isa_and_present<omp::LoopWrapperInterface>(op) &&6435 !dyn_cast_if_present<omp::LoopWrapperInterface>(op->getParentOp());6436 6437 if (isOutermostLoopWrapper)6438 moduleTranslation.stackPush<OpenMPLoopInfoStackFrame>();6439 6440 auto result =6441 llvm::TypeSwitch<Operation *, LogicalResult>(op)6442 .Case([&](omp::BarrierOp op) -> LogicalResult {6443 if (failed(checkImplementationStatus(*op)))6444 return failure();6445 6446 llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =6447 ompBuilder->createBarrier(builder.saveIP(),6448 llvm::omp::OMPD_barrier);6449 LogicalResult res = handleError(afterIP, *op);6450 if (res.succeeded()) {6451 // If the barrier generated a cancellation check, the insertion6452 // point might now need to be changed to a new continuation block6453 builder.restoreIP(*afterIP);6454 }6455 return res;6456 })6457 .Case([&](omp::TaskyieldOp op) {6458 if (failed(checkImplementationStatus(*op)))6459 return failure();6460 6461 ompBuilder->createTaskyield(builder.saveIP());6462 return success();6463 })6464 .Case([&](omp::FlushOp op) {6465 if (failed(checkImplementationStatus(*op)))6466 return failure();6467 6468 // No support in Openmp runtime function (__kmpc_flush) to accept6469 // the argument list.6470 // OpenMP standard states the following:6471 // "An implementation may implement a flush with a list by ignoring6472 // the list, and treating it the same as a flush without a list."6473 //6474 // The argument list is discarded so that, flush with a list is6475 // treated same as a flush without a list.6476 ompBuilder->createFlush(builder.saveIP());6477 return success();6478 })6479 .Case([&](omp::ParallelOp op) {6480 return convertOmpParallel(op, builder, moduleTranslation);6481 })6482 .Case([&](omp::MaskedOp) {6483 return convertOmpMasked(*op, builder, moduleTranslation);6484 })6485 .Case([&](omp::MasterOp) {6486 return convertOmpMaster(*op, builder, moduleTranslation);6487 })6488 .Case([&](omp::CriticalOp) {6489 return convertOmpCritical(*op, builder, moduleTranslation);6490 })6491 .Case([&](omp::OrderedRegionOp) {6492 return convertOmpOrderedRegion(*op, builder, moduleTranslation);6493 })6494 .Case([&](omp::OrderedOp) {6495 return convertOmpOrdered(*op, builder, moduleTranslation);6496 })6497 .Case([&](omp::WsloopOp) {6498 return convertOmpWsloop(*op, builder, moduleTranslation);6499 })6500 .Case([&](omp::SimdOp) {6501 return convertOmpSimd(*op, builder, moduleTranslation);6502 })6503 .Case([&](omp::AtomicReadOp) {6504 return convertOmpAtomicRead(*op, builder, moduleTranslation);6505 })6506 .Case([&](omp::AtomicWriteOp) {6507 return convertOmpAtomicWrite(*op, builder, moduleTranslation);6508 })6509 .Case([&](omp::AtomicUpdateOp op) {6510 return convertOmpAtomicUpdate(op, builder, moduleTranslation);6511 })6512 .Case([&](omp::AtomicCaptureOp op) {6513 return convertOmpAtomicCapture(op, builder, moduleTranslation);6514 })6515 .Case([&](omp::CancelOp op) {6516 return convertOmpCancel(op, builder, moduleTranslation);6517 })6518 .Case([&](omp::CancellationPointOp op) {6519 return convertOmpCancellationPoint(op, builder, moduleTranslation);6520 })6521 .Case([&](omp::SectionsOp) {6522 return convertOmpSections(*op, builder, moduleTranslation);6523 })6524 .Case([&](omp::SingleOp op) {6525 return convertOmpSingle(op, builder, moduleTranslation);6526 })6527 .Case([&](omp::TeamsOp op) {6528 return convertOmpTeams(op, builder, moduleTranslation);6529 })6530 .Case([&](omp::TaskOp op) {6531 return convertOmpTaskOp(op, builder, moduleTranslation);6532 })6533 .Case([&](omp::TaskgroupOp op) {6534 return convertOmpTaskgroupOp(op, builder, moduleTranslation);6535 })6536 .Case([&](omp::TaskwaitOp op) {6537 return convertOmpTaskwaitOp(op, builder, moduleTranslation);6538 })6539 .Case<omp::YieldOp, omp::TerminatorOp, omp::DeclareMapperOp,6540 omp::DeclareMapperInfoOp, omp::DeclareReductionOp,6541 omp::CriticalDeclareOp>([](auto op) {6542 // `yield` and `terminator` can be just omitted. The block structure6543 // was created in the region that handles their parent operation.6544 // `declare_reduction` will be used by reductions and is not6545 // converted directly, skip it.6546 // `declare_mapper` and `declare_mapper.info` are handled whenever6547 // they are referred to through a `map` clause.6548 // `critical.declare` is only used to declare names of critical6549 // sections which will be used by `critical` ops and hence can be6550 // ignored for lowering. The OpenMP IRBuilder will create unique6551 // name for critical section names.6552 return success();6553 })6554 .Case([&](omp::ThreadprivateOp) {6555 return convertOmpThreadprivate(*op, builder, moduleTranslation);6556 })6557 .Case<omp::TargetDataOp, omp::TargetEnterDataOp,6558 omp::TargetExitDataOp, omp::TargetUpdateOp>([&](auto op) {6559 return convertOmpTargetData(op, builder, moduleTranslation);6560 })6561 .Case([&](omp::TargetOp) {6562 return convertOmpTarget(*op, builder, moduleTranslation);6563 })6564 .Case([&](omp::DistributeOp) {6565 return convertOmpDistribute(*op, builder, moduleTranslation);6566 })6567 .Case([&](omp::LoopNestOp) {6568 return convertOmpLoopNest(*op, builder, moduleTranslation);6569 })6570 .Case<omp::MapInfoOp, omp::MapBoundsOp, omp::PrivateClauseOp>(6571 [&](auto op) {6572 // No-op, should be handled by relevant owning operations e.g.6573 // TargetOp, TargetEnterDataOp, TargetExitDataOp, TargetDataOp6574 // etc. and then discarded6575 return success();6576 })6577 .Case([&](omp::NewCliOp op) {6578 // Meta-operation: Doesn't do anything by itself, but used to6579 // identify a loop.6580 return success();6581 })6582 .Case([&](omp::CanonicalLoopOp op) {6583 return convertOmpCanonicalLoopOp(op, builder, moduleTranslation);6584 })6585 .Case([&](omp::UnrollHeuristicOp op) {6586 // FIXME: Handling omp.unroll_heuristic as an executable requires6587 // that the generator (e.g. omp.canonical_loop) has been seen first.6588 // For construct that require all codegen to occur inside a callback6589 // (e.g. OpenMPIRBilder::createParallel), all codegen of that6590 // contained region including their transformations must occur at6591 // the omp.canonical_loop.6592 return applyUnrollHeuristic(op, builder, moduleTranslation);6593 })6594 .Case([&](omp::TileOp op) {6595 return applyTile(op, builder, moduleTranslation);6596 })6597 .Case([&](omp::TargetAllocMemOp) {6598 return convertTargetAllocMemOp(*op, builder, moduleTranslation);6599 })6600 .Case([&](omp::TargetFreeMemOp) {6601 return convertTargetFreeMemOp(*op, builder, moduleTranslation);6602 })6603 .Default([&](Operation *inst) {6604 return inst->emitError()6605 << "not yet implemented: " << inst->getName();6606 });6607 6608 if (isOutermostLoopWrapper)6609 moduleTranslation.stackPop();6610 6611 return result;6612}6613 6614static LogicalResult6615convertTargetDeviceOp(Operation *op, llvm::IRBuilderBase &builder,6616 LLVM::ModuleTranslation &moduleTranslation) {6617 return convertHostOrTargetOperation(op, builder, moduleTranslation);6618}6619 6620static LogicalResult6621convertTargetOpsInNest(Operation *op, llvm::IRBuilderBase &builder,6622 LLVM::ModuleTranslation &moduleTranslation) {6623 if (isa<omp::TargetOp>(op))6624 return convertOmpTarget(*op, builder, moduleTranslation);6625 if (isa<omp::TargetDataOp>(op))6626 return convertOmpTargetData(op, builder, moduleTranslation);6627 bool interrupted =6628 op->walk<WalkOrder::PreOrder>([&](Operation *oper) {6629 if (isa<omp::TargetOp>(oper)) {6630 if (failed(convertOmpTarget(*oper, builder, moduleTranslation)))6631 return WalkResult::interrupt();6632 return WalkResult::skip();6633 }6634 if (isa<omp::TargetDataOp>(oper)) {6635 if (failed(convertOmpTargetData(oper, builder, moduleTranslation)))6636 return WalkResult::interrupt();6637 return WalkResult::skip();6638 }6639 6640 // Non-target ops might nest target-related ops, therefore, we6641 // translate them as non-OpenMP scopes. Translating them is needed by6642 // nested target-related ops since they might need LLVM values defined6643 // in their parent non-target ops.6644 if (isa<omp::OpenMPDialect>(oper->getDialect()) &&6645 oper->getParentOfType<LLVM::LLVMFuncOp>() &&6646 !oper->getRegions().empty()) {6647 if (auto blockArgsIface =6648 dyn_cast<omp::BlockArgOpenMPOpInterface>(oper))6649 forwardArgs(moduleTranslation, blockArgsIface);6650 else {6651 // Here we map entry block arguments of6652 // non-BlockArgOpenMPOpInterface ops if they can be encountered6653 // inside of a function and they define any of these arguments.6654 if (isa<mlir::omp::AtomicUpdateOp>(oper))6655 for (auto [operand, arg] :6656 llvm::zip_equal(oper->getOperands(),6657 oper->getRegion(0).getArguments())) {6658 moduleTranslation.mapValue(6659 arg, builder.CreateLoad(6660 moduleTranslation.convertType(arg.getType()),6661 moduleTranslation.lookupValue(operand)));6662 }6663 }6664 6665 if (auto loopNest = dyn_cast<omp::LoopNestOp>(oper)) {6666 assert(builder.GetInsertBlock() &&6667 "No insert block is set for the builder");6668 for (auto iv : loopNest.getIVs()) {6669 // Map iv to an undefined value just to keep the IR validity.6670 moduleTranslation.mapValue(6671 iv, llvm::PoisonValue::get(6672 moduleTranslation.convertType(iv.getType())));6673 }6674 }6675 6676 for (Region ®ion : oper->getRegions()) {6677 // Regions are fake in the sense that they are not a truthful6678 // translation of the OpenMP construct being converted (e.g. no6679 // OpenMP runtime calls will be generated). We just need this to6680 // prepare the kernel invocation args.6681 SmallVector<llvm::PHINode *> phis;6682 auto result = convertOmpOpRegions(6683 region, oper->getName().getStringRef().str() + ".fake.region",6684 builder, moduleTranslation, &phis);6685 if (failed(handleError(result, *oper)))6686 return WalkResult::interrupt();6687 6688 builder.SetInsertPoint(result.get(), result.get()->end());6689 }6690 6691 return WalkResult::skip();6692 }6693 6694 return WalkResult::advance();6695 }).wasInterrupted();6696 return failure(interrupted);6697}6698 6699namespace {6700 6701/// Implementation of the dialect interface that converts operations belonging6702/// to the OpenMP dialect to LLVM IR.6703class OpenMPDialectLLVMIRTranslationInterface6704 : public LLVMTranslationDialectInterface {6705public:6706 using LLVMTranslationDialectInterface::LLVMTranslationDialectInterface;6707 6708 /// Translates the given operation to LLVM IR using the provided IR builder6709 /// and saving the state in `moduleTranslation`.6710 LogicalResult6711 convertOperation(Operation *op, llvm::IRBuilderBase &builder,6712 LLVM::ModuleTranslation &moduleTranslation) const final;6713 6714 /// Given an OpenMP MLIR attribute, create the corresponding LLVM-IR,6715 /// runtime calls, or operation amendments6716 LogicalResult6717 amendOperation(Operation *op, ArrayRef<llvm::Instruction *> instructions,6718 NamedAttribute attribute,6719 LLVM::ModuleTranslation &moduleTranslation) const final;6720};6721 6722} // namespace6723 6724LogicalResult OpenMPDialectLLVMIRTranslationInterface::amendOperation(6725 Operation *op, ArrayRef<llvm::Instruction *> instructions,6726 NamedAttribute attribute,6727 LLVM::ModuleTranslation &moduleTranslation) const {6728 return llvm::StringSwitch<llvm::function_ref<LogicalResult(Attribute)>>(6729 attribute.getName())6730 .Case("omp.is_target_device",6731 [&](Attribute attr) {6732 if (auto deviceAttr = dyn_cast<BoolAttr>(attr)) {6733 llvm::OpenMPIRBuilderConfig &config =6734 moduleTranslation.getOpenMPBuilder()->Config;6735 config.setIsTargetDevice(deviceAttr.getValue());6736 return success();6737 }6738 return failure();6739 })6740 .Case("omp.is_gpu",6741 [&](Attribute attr) {6742 if (auto gpuAttr = dyn_cast<BoolAttr>(attr)) {6743 llvm::OpenMPIRBuilderConfig &config =6744 moduleTranslation.getOpenMPBuilder()->Config;6745 config.setIsGPU(gpuAttr.getValue());6746 return success();6747 }6748 return failure();6749 })6750 .Case("omp.host_ir_filepath",6751 [&](Attribute attr) {6752 if (auto filepathAttr = dyn_cast<StringAttr>(attr)) {6753 llvm::OpenMPIRBuilder *ompBuilder =6754 moduleTranslation.getOpenMPBuilder();6755 auto VFS = llvm::vfs::getRealFileSystem();6756 ompBuilder->loadOffloadInfoMetadata(*VFS,6757 filepathAttr.getValue());6758 return success();6759 }6760 return failure();6761 })6762 .Case("omp.flags",6763 [&](Attribute attr) {6764 if (auto rtlAttr = dyn_cast<omp::FlagsAttr>(attr))6765 return convertFlagsAttr(op, rtlAttr, moduleTranslation);6766 return failure();6767 })6768 .Case("omp.version",6769 [&](Attribute attr) {6770 if (auto versionAttr = dyn_cast<omp::VersionAttr>(attr)) {6771 llvm::OpenMPIRBuilder *ompBuilder =6772 moduleTranslation.getOpenMPBuilder();6773 ompBuilder->M.addModuleFlag(llvm::Module::Max, "openmp",6774 versionAttr.getVersion());6775 return success();6776 }6777 return failure();6778 })6779 .Case("omp.declare_target",6780 [&](Attribute attr) {6781 if (auto declareTargetAttr =6782 dyn_cast<omp::DeclareTargetAttr>(attr))6783 return convertDeclareTargetAttr(op, declareTargetAttr,6784 moduleTranslation);6785 return failure();6786 })6787 .Case("omp.requires",6788 [&](Attribute attr) {6789 if (auto requiresAttr = dyn_cast<omp::ClauseRequiresAttr>(attr)) {6790 using Requires = omp::ClauseRequires;6791 Requires flags = requiresAttr.getValue();6792 llvm::OpenMPIRBuilderConfig &config =6793 moduleTranslation.getOpenMPBuilder()->Config;6794 config.setHasRequiresReverseOffload(6795 bitEnumContainsAll(flags, Requires::reverse_offload));6796 config.setHasRequiresUnifiedAddress(6797 bitEnumContainsAll(flags, Requires::unified_address));6798 config.setHasRequiresUnifiedSharedMemory(6799 bitEnumContainsAll(flags, Requires::unified_shared_memory));6800 config.setHasRequiresDynamicAllocators(6801 bitEnumContainsAll(flags, Requires::dynamic_allocators));6802 return success();6803 }6804 return failure();6805 })6806 .Case("omp.target_triples",6807 [&](Attribute attr) {6808 if (auto triplesAttr = dyn_cast<ArrayAttr>(attr)) {6809 llvm::OpenMPIRBuilderConfig &config =6810 moduleTranslation.getOpenMPBuilder()->Config;6811 config.TargetTriples.clear();6812 config.TargetTriples.reserve(triplesAttr.size());6813 for (Attribute tripleAttr : triplesAttr) {6814 if (auto tripleStrAttr = dyn_cast<StringAttr>(tripleAttr))6815 config.TargetTriples.emplace_back(tripleStrAttr.getValue());6816 else6817 return failure();6818 }6819 return success();6820 }6821 return failure();6822 })6823 .Default([](Attribute) {6824 // Fall through for omp attributes that do not require lowering.6825 return success();6826 })(attribute.getValue());6827 6828 return failure();6829}6830 6831/// Given an OpenMP MLIR operation, create the corresponding LLVM IR6832/// (including OpenMP runtime calls).6833LogicalResult OpenMPDialectLLVMIRTranslationInterface::convertOperation(6834 Operation *op, llvm::IRBuilderBase &builder,6835 LLVM::ModuleTranslation &moduleTranslation) const {6836 6837 llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();6838 if (ompBuilder->Config.isTargetDevice()) {6839 if (isTargetDeviceOp(op)) {6840 return convertTargetDeviceOp(op, builder, moduleTranslation);6841 }6842 return convertTargetOpsInNest(op, builder, moduleTranslation);6843 }6844 return convertHostOrTargetOperation(op, builder, moduleTranslation);6845}6846 6847void mlir::registerOpenMPDialectTranslation(DialectRegistry ®istry) {6848 registry.insert<omp::OpenMPDialect>();6849 registry.addExtension(+[](MLIRContext *ctx, omp::OpenMPDialect *dialect) {6850 dialect->addInterfaces<OpenMPDialectLLVMIRTranslationInterface>();6851 });6852}6853 6854void mlir::registerOpenMPDialectTranslation(MLIRContext &context) {6855 DialectRegistry registry;6856 registerOpenMPDialectTranslation(registry);6857 context.appendDialectRegistry(registry);6858}6859