11131 lines · cpp
1//===- OpenMPIRBuilder.cpp - Builder for LLVM-IR for OpenMP directives ----===//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/// \file9///10/// This file implements the OpenMPIRBuilder class, which is used as a11/// convenient way to create LLVM instructions for OpenMP directives.12///13//===----------------------------------------------------------------------===//14 15#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"16#include "llvm/ADT/SmallBitVector.h"17#include "llvm/ADT/SmallSet.h"18#include "llvm/ADT/StringExtras.h"19#include "llvm/ADT/StringRef.h"20#include "llvm/Analysis/AssumptionCache.h"21#include "llvm/Analysis/CodeMetrics.h"22#include "llvm/Analysis/LoopInfo.h"23#include "llvm/Analysis/OptimizationRemarkEmitter.h"24#include "llvm/Analysis/ScalarEvolution.h"25#include "llvm/Analysis/TargetLibraryInfo.h"26#include "llvm/Bitcode/BitcodeReader.h"27#include "llvm/Frontend/Offloading/Utility.h"28#include "llvm/Frontend/OpenMP/OMPGridValues.h"29#include "llvm/IR/Attributes.h"30#include "llvm/IR/BasicBlock.h"31#include "llvm/IR/CFG.h"32#include "llvm/IR/CallingConv.h"33#include "llvm/IR/Constant.h"34#include "llvm/IR/Constants.h"35#include "llvm/IR/DIBuilder.h"36#include "llvm/IR/DebugInfoMetadata.h"37#include "llvm/IR/DerivedTypes.h"38#include "llvm/IR/Function.h"39#include "llvm/IR/GlobalVariable.h"40#include "llvm/IR/IRBuilder.h"41#include "llvm/IR/InstIterator.h"42#include "llvm/IR/IntrinsicInst.h"43#include "llvm/IR/LLVMContext.h"44#include "llvm/IR/MDBuilder.h"45#include "llvm/IR/Metadata.h"46#include "llvm/IR/PassInstrumentation.h"47#include "llvm/IR/PassManager.h"48#include "llvm/IR/ReplaceConstant.h"49#include "llvm/IR/Value.h"50#include "llvm/MC/TargetRegistry.h"51#include "llvm/Support/CommandLine.h"52#include "llvm/Support/ErrorHandling.h"53#include "llvm/Support/FileSystem.h"54#include "llvm/Support/VirtualFileSystem.h"55#include "llvm/Target/TargetMachine.h"56#include "llvm/Target/TargetOptions.h"57#include "llvm/Transforms/Utils/BasicBlockUtils.h"58#include "llvm/Transforms/Utils/Cloning.h"59#include "llvm/Transforms/Utils/CodeExtractor.h"60#include "llvm/Transforms/Utils/LoopPeel.h"61#include "llvm/Transforms/Utils/UnrollLoop.h"62 63#include <cstdint>64#include <optional>65 66#define DEBUG_TYPE "openmp-ir-builder"67 68using namespace llvm;69using namespace omp;70 71static cl::opt<bool>72 OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden,73 cl::desc("Use optimistic attributes describing "74 "'as-if' properties of runtime calls."),75 cl::init(false));76 77static cl::opt<double> UnrollThresholdFactor(78 "openmp-ir-builder-unroll-threshold-factor", cl::Hidden,79 cl::desc("Factor for the unroll threshold to account for code "80 "simplifications still taking place"),81 cl::init(1.5));82 83#ifndef NDEBUG84/// Return whether IP1 and IP2 are ambiguous, i.e. that inserting instructions85/// at position IP1 may change the meaning of IP2 or vice-versa. This is because86/// an InsertPoint stores the instruction before something is inserted. For87/// instance, if both point to the same instruction, two IRBuilders alternating88/// creating instruction will cause the instructions to be interleaved.89static bool isConflictIP(IRBuilder<>::InsertPoint IP1,90 IRBuilder<>::InsertPoint IP2) {91 if (!IP1.isSet() || !IP2.isSet())92 return false;93 return IP1.getBlock() == IP2.getBlock() && IP1.getPoint() == IP2.getPoint();94}95 96static bool isValidWorkshareLoopScheduleType(OMPScheduleType SchedType) {97 // Valid ordered/unordered and base algorithm combinations.98 switch (SchedType & ~OMPScheduleType::MonotonicityMask) {99 case OMPScheduleType::UnorderedStaticChunked:100 case OMPScheduleType::UnorderedStatic:101 case OMPScheduleType::UnorderedDynamicChunked:102 case OMPScheduleType::UnorderedGuidedChunked:103 case OMPScheduleType::UnorderedRuntime:104 case OMPScheduleType::UnorderedAuto:105 case OMPScheduleType::UnorderedTrapezoidal:106 case OMPScheduleType::UnorderedGreedy:107 case OMPScheduleType::UnorderedBalanced:108 case OMPScheduleType::UnorderedGuidedIterativeChunked:109 case OMPScheduleType::UnorderedGuidedAnalyticalChunked:110 case OMPScheduleType::UnorderedSteal:111 case OMPScheduleType::UnorderedStaticBalancedChunked:112 case OMPScheduleType::UnorderedGuidedSimd:113 case OMPScheduleType::UnorderedRuntimeSimd:114 case OMPScheduleType::OrderedStaticChunked:115 case OMPScheduleType::OrderedStatic:116 case OMPScheduleType::OrderedDynamicChunked:117 case OMPScheduleType::OrderedGuidedChunked:118 case OMPScheduleType::OrderedRuntime:119 case OMPScheduleType::OrderedAuto:120 case OMPScheduleType::OrderdTrapezoidal:121 case OMPScheduleType::NomergeUnorderedStaticChunked:122 case OMPScheduleType::NomergeUnorderedStatic:123 case OMPScheduleType::NomergeUnorderedDynamicChunked:124 case OMPScheduleType::NomergeUnorderedGuidedChunked:125 case OMPScheduleType::NomergeUnorderedRuntime:126 case OMPScheduleType::NomergeUnorderedAuto:127 case OMPScheduleType::NomergeUnorderedTrapezoidal:128 case OMPScheduleType::NomergeUnorderedGreedy:129 case OMPScheduleType::NomergeUnorderedBalanced:130 case OMPScheduleType::NomergeUnorderedGuidedIterativeChunked:131 case OMPScheduleType::NomergeUnorderedGuidedAnalyticalChunked:132 case OMPScheduleType::NomergeUnorderedSteal:133 case OMPScheduleType::NomergeOrderedStaticChunked:134 case OMPScheduleType::NomergeOrderedStatic:135 case OMPScheduleType::NomergeOrderedDynamicChunked:136 case OMPScheduleType::NomergeOrderedGuidedChunked:137 case OMPScheduleType::NomergeOrderedRuntime:138 case OMPScheduleType::NomergeOrderedAuto:139 case OMPScheduleType::NomergeOrderedTrapezoidal:140 case OMPScheduleType::OrderedDistributeChunked:141 case OMPScheduleType::OrderedDistribute:142 break;143 default:144 return false;145 }146 147 // Must not set both monotonicity modifiers at the same time.148 OMPScheduleType MonotonicityFlags =149 SchedType & OMPScheduleType::MonotonicityMask;150 if (MonotonicityFlags == OMPScheduleType::MonotonicityMask)151 return false;152 153 return true;154}155#endif156 157/// This is wrapper over IRBuilderBase::restoreIP that also restores the current158/// debug location to the last instruction in the specified basic block if the159/// insert point points to the end of the block.160static void restoreIPandDebugLoc(llvm::IRBuilderBase &Builder,161 llvm::IRBuilderBase::InsertPoint IP) {162 Builder.restoreIP(IP);163 llvm::BasicBlock *BB = Builder.GetInsertBlock();164 llvm::BasicBlock::iterator I = Builder.GetInsertPoint();165 if (!BB->empty() && I == BB->end())166 Builder.SetCurrentDebugLocation(BB->back().getStableDebugLoc());167}168 169static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {170 if (T.isAMDGPU()) {171 StringRef Features =172 Kernel->getFnAttribute("target-features").getValueAsString();173 if (Features.count("+wavefrontsize64"))174 return omp::getAMDGPUGridValues<64>();175 return omp::getAMDGPUGridValues<32>();176 }177 if (T.isNVPTX())178 return omp::NVPTXGridValues;179 if (T.isSPIRV())180 return omp::SPIRVGridValues;181 llvm_unreachable("No grid value available for this architecture!");182}183 184/// Determine which scheduling algorithm to use, determined from schedule clause185/// arguments.186static OMPScheduleType187getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,188 bool HasSimdModifier, bool HasDistScheduleChunks) {189 // Currently, the default schedule it static.190 switch (ClauseKind) {191 case OMP_SCHEDULE_Default:192 case OMP_SCHEDULE_Static:193 return HasChunks ? OMPScheduleType::BaseStaticChunked194 : OMPScheduleType::BaseStatic;195 case OMP_SCHEDULE_Dynamic:196 return OMPScheduleType::BaseDynamicChunked;197 case OMP_SCHEDULE_Guided:198 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd199 : OMPScheduleType::BaseGuidedChunked;200 case OMP_SCHEDULE_Auto:201 return llvm::omp::OMPScheduleType::BaseAuto;202 case OMP_SCHEDULE_Runtime:203 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd204 : OMPScheduleType::BaseRuntime;205 case OMP_SCHEDULE_Distribute:206 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked207 : OMPScheduleType::BaseDistribute;208 }209 llvm_unreachable("unhandled schedule clause argument");210}211 212/// Adds ordering modifier flags to schedule type.213static OMPScheduleType214getOpenMPOrderingScheduleType(OMPScheduleType BaseScheduleType,215 bool HasOrderedClause) {216 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==217 OMPScheduleType::None &&218 "Must not have ordering nor monotonicity flags already set");219 220 OMPScheduleType OrderingModifier = HasOrderedClause221 ? OMPScheduleType::ModifierOrdered222 : OMPScheduleType::ModifierUnordered;223 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;224 225 // Unsupported combinations226 if (OrderingScheduleType ==227 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))228 return OMPScheduleType::OrderedGuidedChunked;229 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |230 OMPScheduleType::ModifierOrdered))231 return OMPScheduleType::OrderedRuntime;232 233 return OrderingScheduleType;234}235 236/// Adds monotonicity modifier flags to schedule type.237static OMPScheduleType238getOpenMPMonotonicityScheduleType(OMPScheduleType ScheduleType,239 bool HasSimdModifier, bool HasMonotonic,240 bool HasNonmonotonic, bool HasOrderedClause) {241 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==242 OMPScheduleType::None &&243 "Must not have monotonicity flags already set");244 assert((!HasMonotonic || !HasNonmonotonic) &&245 "Monotonic and Nonmonotonic are contradicting each other");246 247 if (HasMonotonic) {248 return ScheduleType | OMPScheduleType::ModifierMonotonic;249 } else if (HasNonmonotonic) {250 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;251 } else {252 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.253 // If the static schedule kind is specified or if the ordered clause is254 // specified, and if the nonmonotonic modifier is not specified, the255 // effect is as if the monotonic modifier is specified. Otherwise, unless256 // the monotonic modifier is specified, the effect is as if the257 // nonmonotonic modifier is specified.258 OMPScheduleType BaseScheduleType =259 ScheduleType & ~OMPScheduleType::ModifierMask;260 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||261 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||262 HasOrderedClause) {263 // The monotonic is used by default in openmp runtime library, so no need264 // to set it.265 return ScheduleType;266 } else {267 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;268 }269 }270}271 272/// Determine the schedule type using schedule and ordering clause arguments.273static OMPScheduleType274computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,275 bool HasSimdModifier, bool HasMonotonicModifier,276 bool HasNonmonotonicModifier, bool HasOrderedClause,277 bool HasDistScheduleChunks) {278 OMPScheduleType BaseSchedule = getOpenMPBaseScheduleType(279 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);280 OMPScheduleType OrderedSchedule =281 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);282 OMPScheduleType Result = getOpenMPMonotonicityScheduleType(283 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,284 HasNonmonotonicModifier, HasOrderedClause);285 286 assert(isValidWorkshareLoopScheduleType(Result));287 return Result;288}289 290/// Make \p Source branch to \p Target.291///292/// Handles two situations:293/// * \p Source already has an unconditional branch.294/// * \p Source is a degenerate block (no terminator because the BB is295/// the current head of the IR construction).296static void redirectTo(BasicBlock *Source, BasicBlock *Target, DebugLoc DL) {297 if (Instruction *Term = Source->getTerminator()) {298 auto *Br = cast<BranchInst>(Term);299 assert(!Br->isConditional() &&300 "BB's terminator must be an unconditional branch (or degenerate)");301 BasicBlock *Succ = Br->getSuccessor(0);302 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);303 Br->setSuccessor(0, Target);304 return;305 }306 307 auto *NewBr = BranchInst::Create(Target, Source);308 NewBr->setDebugLoc(DL);309}310 311void llvm::spliceBB(IRBuilderBase::InsertPoint IP, BasicBlock *New,312 bool CreateBranch, DebugLoc DL) {313 assert(New->getFirstInsertionPt() == New->begin() &&314 "Target BB must not have PHI nodes");315 316 // Move instructions to new block.317 BasicBlock *Old = IP.getBlock();318 // If the `Old` block is empty then there are no instructions to move. But in319 // the new debug scheme, it could have trailing debug records which will be320 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2321 // reasons:322 // 1. If `New` is also empty, `BasicBlock::splice` crashes.323 // 2. Even if `New` is not empty, the rationale to move those records to `New`324 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function325 // assumes that `Old` is optimized out and is going away. This is not the case326 // here. The `Old` block is still being used e.g. a branch instruction is327 // added to it later in this function.328 // So we call `BasicBlock::splice` only when `Old` is not empty.329 if (!Old->empty())330 New->splice(New->begin(), Old, IP.getPoint(), Old->end());331 332 if (CreateBranch) {333 auto *NewBr = BranchInst::Create(New, Old);334 NewBr->setDebugLoc(DL);335 }336}337 338void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {339 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();340 BasicBlock *Old = Builder.GetInsertBlock();341 342 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);343 if (CreateBranch)344 Builder.SetInsertPoint(Old->getTerminator());345 else346 Builder.SetInsertPoint(Old);347 348 // SetInsertPoint also updates the Builder's debug location, but we want to349 // keep the one the Builder was configured to use.350 Builder.SetCurrentDebugLocation(DebugLoc);351}352 353BasicBlock *llvm::splitBB(IRBuilderBase::InsertPoint IP, bool CreateBranch,354 DebugLoc DL, llvm::Twine Name) {355 BasicBlock *Old = IP.getBlock();356 BasicBlock *New = BasicBlock::Create(357 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,358 Old->getParent(), Old->getNextNode());359 spliceBB(IP, New, CreateBranch, DL);360 New->replaceSuccessorsPhiUsesWith(Old, New);361 return New;362}363 364BasicBlock *llvm::splitBB(IRBuilderBase &Builder, bool CreateBranch,365 llvm::Twine Name) {366 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();367 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);368 if (CreateBranch)369 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());370 else371 Builder.SetInsertPoint(Builder.GetInsertBlock());372 // SetInsertPoint also updates the Builder's debug location, but we want to373 // keep the one the Builder was configured to use.374 Builder.SetCurrentDebugLocation(DebugLoc);375 return New;376}377 378BasicBlock *llvm::splitBB(IRBuilder<> &Builder, bool CreateBranch,379 llvm::Twine Name) {380 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();381 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);382 if (CreateBranch)383 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());384 else385 Builder.SetInsertPoint(Builder.GetInsertBlock());386 // SetInsertPoint also updates the Builder's debug location, but we want to387 // keep the one the Builder was configured to use.388 Builder.SetCurrentDebugLocation(DebugLoc);389 return New;390}391 392BasicBlock *llvm::splitBBWithSuffix(IRBuilderBase &Builder, bool CreateBranch,393 llvm::Twine Suffix) {394 BasicBlock *Old = Builder.GetInsertBlock();395 return splitBB(Builder, CreateBranch, Old->getName() + Suffix);396}397 398// This function creates a fake integer value and a fake use for the integer399// value. It returns the fake value created. This is useful in modeling the400// extra arguments to the outlined functions.401Value *createFakeIntVal(IRBuilderBase &Builder,402 OpenMPIRBuilder::InsertPointTy OuterAllocaIP,403 llvm::SmallVectorImpl<Instruction *> &ToBeDeleted,404 OpenMPIRBuilder::InsertPointTy InnerAllocaIP,405 const Twine &Name = "", bool AsPtr = true) {406 Builder.restoreIP(OuterAllocaIP);407 Instruction *FakeVal;408 AllocaInst *FakeValAddr =409 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, Name + ".addr");410 ToBeDeleted.push_back(FakeValAddr);411 412 if (AsPtr) {413 FakeVal = FakeValAddr;414 } else {415 FakeVal =416 Builder.CreateLoad(Builder.getInt32Ty(), FakeValAddr, Name + ".val");417 ToBeDeleted.push_back(FakeVal);418 }419 420 // Generate a fake use of this value421 Builder.restoreIP(InnerAllocaIP);422 Instruction *UseFakeVal;423 if (AsPtr) {424 UseFakeVal =425 Builder.CreateLoad(Builder.getInt32Ty(), FakeVal, Name + ".use");426 } else {427 UseFakeVal =428 cast<BinaryOperator>(Builder.CreateAdd(FakeVal, Builder.getInt32(10)));429 }430 ToBeDeleted.push_back(UseFakeVal);431 return FakeVal;432}433 434//===----------------------------------------------------------------------===//435// OpenMPIRBuilderConfig436//===----------------------------------------------------------------------===//437 438namespace {439LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();440/// Values for bit flags for marking which requires clauses have been used.441enum OpenMPOffloadingRequiresDirFlags {442 /// flag undefined.443 OMP_REQ_UNDEFINED = 0x000,444 /// no requires directive present.445 OMP_REQ_NONE = 0x001,446 /// reverse_offload clause.447 OMP_REQ_REVERSE_OFFLOAD = 0x002,448 /// unified_address clause.449 OMP_REQ_UNIFIED_ADDRESS = 0x004,450 /// unified_shared_memory clause.451 OMP_REQ_UNIFIED_SHARED_MEMORY = 0x008,452 /// dynamic_allocators clause.453 OMP_REQ_DYNAMIC_ALLOCATORS = 0x010,454 LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)455};456 457} // anonymous namespace458 459OpenMPIRBuilderConfig::OpenMPIRBuilderConfig()460 : RequiresFlags(OMP_REQ_UNDEFINED) {}461 462OpenMPIRBuilderConfig::OpenMPIRBuilderConfig(463 bool IsTargetDevice, bool IsGPU, bool OpenMPOffloadMandatory,464 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,465 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)466 : IsTargetDevice(IsTargetDevice), IsGPU(IsGPU),467 OpenMPOffloadMandatory(OpenMPOffloadMandatory),468 RequiresFlags(OMP_REQ_UNDEFINED) {469 if (HasRequiresReverseOffload)470 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;471 if (HasRequiresUnifiedAddress)472 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;473 if (HasRequiresUnifiedSharedMemory)474 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;475 if (HasRequiresDynamicAllocators)476 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;477}478 479bool OpenMPIRBuilderConfig::hasRequiresReverseOffload() const {480 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;481}482 483bool OpenMPIRBuilderConfig::hasRequiresUnifiedAddress() const {484 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;485}486 487bool OpenMPIRBuilderConfig::hasRequiresUnifiedSharedMemory() const {488 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;489}490 491bool OpenMPIRBuilderConfig::hasRequiresDynamicAllocators() const {492 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;493}494 495int64_t OpenMPIRBuilderConfig::getRequiresFlags() const {496 return hasRequiresFlags() ? RequiresFlags497 : static_cast<int64_t>(OMP_REQ_NONE);498}499 500void OpenMPIRBuilderConfig::setHasRequiresReverseOffload(bool Value) {501 if (Value)502 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;503 else504 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;505}506 507void OpenMPIRBuilderConfig::setHasRequiresUnifiedAddress(bool Value) {508 if (Value)509 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;510 else511 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;512}513 514void OpenMPIRBuilderConfig::setHasRequiresUnifiedSharedMemory(bool Value) {515 if (Value)516 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;517 else518 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;519}520 521void OpenMPIRBuilderConfig::setHasRequiresDynamicAllocators(bool Value) {522 if (Value)523 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;524 else525 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;526}527 528//===----------------------------------------------------------------------===//529// OpenMPIRBuilder530//===----------------------------------------------------------------------===//531 532void OpenMPIRBuilder::getKernelArgsVector(TargetKernelArgs &KernelArgs,533 IRBuilderBase &Builder,534 SmallVector<Value *> &ArgsVector) {535 Value *Version = Builder.getInt32(OMP_KERNEL_ARG_VERSION);536 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);537 auto Int32Ty = Type::getInt32Ty(Builder.getContext());538 constexpr size_t MaxDim = 3;539 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));540 541 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);542 543 Value *DynCGroupMemFallbackFlag =544 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));545 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);546 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);547 548 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());549 550 Value *NumTeams3D =551 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});552 Value *NumThreads3D =553 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});554 for (unsigned I :555 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))556 NumTeams3D =557 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});558 for (unsigned I :559 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))560 NumThreads3D =561 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});562 563 ArgsVector = {Version,564 PointerNum,565 KernelArgs.RTArgs.BasePointersArray,566 KernelArgs.RTArgs.PointersArray,567 KernelArgs.RTArgs.SizesArray,568 KernelArgs.RTArgs.MapTypesArray,569 KernelArgs.RTArgs.MapNamesArray,570 KernelArgs.RTArgs.MappersArray,571 KernelArgs.NumIterations,572 Flags,573 NumTeams3D,574 NumThreads3D,575 KernelArgs.DynCGroupMem};576}577 578void OpenMPIRBuilder::addAttributes(omp::RuntimeFunction FnID, Function &Fn) {579 LLVMContext &Ctx = Fn.getContext();580 581 // Get the function's current attributes.582 auto Attrs = Fn.getAttributes();583 auto FnAttrs = Attrs.getFnAttrs();584 auto RetAttrs = Attrs.getRetAttrs();585 SmallVector<AttributeSet, 4> ArgAttrs;586 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)587 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));588 589 // Add AS to FnAS while taking special care with integer extensions.590 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,591 bool Param = true) -> void {592 bool HasSignExt = AS.hasAttribute(Attribute::SExt);593 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);594 if (HasSignExt || HasZeroExt) {595 assert(AS.getNumAttributes() == 1 &&596 "Currently not handling extension attr combined with others.");597 if (Param) {598 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))599 FnAS = FnAS.addAttribute(Ctx, AK);600 } else if (auto AK =601 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))602 FnAS = FnAS.addAttribute(Ctx, AK);603 } else {604 FnAS = FnAS.addAttributes(Ctx, AS);605 }606 };607 608#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;609#include "llvm/Frontend/OpenMP/OMPKinds.def"610 611 // Add attributes to the function declaration.612 switch (FnID) {613#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \614 case Enum: \615 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \616 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \617 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \618 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \619 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \620 break;621#include "llvm/Frontend/OpenMP/OMPKinds.def"622 default:623 // Attributes are optional.624 break;625 }626}627 628FunctionCallee629OpenMPIRBuilder::getOrCreateRuntimeFunction(Module &M, RuntimeFunction FnID) {630 FunctionType *FnTy = nullptr;631 Function *Fn = nullptr;632 633 // Try to find the declation in the module first.634 switch (FnID) {635#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \636 case Enum: \637 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \638 IsVarArg); \639 Fn = M.getFunction(Str); \640 break;641#include "llvm/Frontend/OpenMP/OMPKinds.def"642 }643 644 if (!Fn) {645 // Create a new declaration if we need one.646 switch (FnID) {647#define OMP_RTL(Enum, Str, ...) \648 case Enum: \649 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \650 break;651#include "llvm/Frontend/OpenMP/OMPKinds.def"652 }653 Fn->setCallingConv(Config.getRuntimeCC());654 // Add information if the runtime function takes a callback function655 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {656 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {657 LLVMContext &Ctx = Fn->getContext();658 MDBuilder MDB(Ctx);659 // Annotate the callback behavior of the runtime function:660 // - The callback callee is argument number 2 (microtask).661 // - The first two arguments of the callback callee are unknown (-1).662 // - All variadic arguments to the runtime function are passed to the663 // callback callee.664 Fn->addMetadata(665 LLVMContext::MD_callback,666 *MDNode::get(Ctx, {MDB.createCallbackEncoding(667 2, {-1, -1}, /* VarArgsArePassed */ true)}));668 }669 }670 671 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()672 << " with type " << *Fn->getFunctionType() << "\n");673 addAttributes(FnID, *Fn);674 675 } else {676 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()677 << " with type " << *Fn->getFunctionType() << "\n");678 }679 680 assert(Fn && "Failed to create OpenMP runtime function");681 682 return {FnTy, Fn};683}684 685Expected<BasicBlock *>686OpenMPIRBuilder::FinalizationInfo::getFiniBB(IRBuilderBase &Builder) {687 if (!FiniBB) {688 Function *ParentFunc = Builder.GetInsertBlock()->getParent();689 IRBuilderBase::InsertPointGuard Guard(Builder);690 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);691 Builder.SetInsertPoint(FiniBB);692 // FiniCB adds the branch to the exit stub.693 if (Error Err = FiniCB(Builder.saveIP()))694 return Err;695 }696 return FiniBB;697}698 699Error OpenMPIRBuilder::FinalizationInfo::mergeFiniBB(IRBuilderBase &Builder,700 BasicBlock *OtherFiniBB) {701 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.702 if (!FiniBB) {703 FiniBB = OtherFiniBB;704 705 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());706 if (Error Err = FiniCB(Builder.saveIP()))707 return Err;708 709 return Error::success();710 }711 712 // Move instructions from FiniBB to the start of OtherFiniBB.713 auto EndIt = FiniBB->end();714 if (FiniBB->size() >= 1)715 if (auto Prev = std::prev(EndIt); Prev->isTerminator())716 EndIt = Prev;717 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),718 EndIt);719 720 FiniBB->replaceAllUsesWith(OtherFiniBB);721 FiniBB->eraseFromParent();722 FiniBB = OtherFiniBB;723 return Error::success();724}725 726Function *OpenMPIRBuilder::getOrCreateRuntimeFunctionPtr(RuntimeFunction FnID) {727 FunctionCallee RTLFn = getOrCreateRuntimeFunction(M, FnID);728 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());729 assert(Fn && "Failed to create OpenMP runtime function pointer");730 return Fn;731}732 733CallInst *OpenMPIRBuilder::createRuntimeFunctionCall(FunctionCallee Callee,734 ArrayRef<Value *> Args,735 StringRef Name) {736 CallInst *Call = Builder.CreateCall(Callee, Args, Name);737 Call->setCallingConv(Config.getRuntimeCC());738 return Call;739}740 741void OpenMPIRBuilder::initialize() { initializeTypes(M); }742 743static void raiseUserConstantDataAllocasToEntryBlock(IRBuilderBase &Builder,744 Function *Function) {745 BasicBlock &EntryBlock = Function->getEntryBlock();746 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();747 748 // Loop over blocks looking for constant allocas, skipping the entry block749 // as any allocas there are already in the desired location.750 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();751 Block++) {752 for (auto Inst = Block->getReverseIterator()->begin();753 Inst != Block->getReverseIterator()->end();) {754 if (auto *AllocaInst = dyn_cast_if_present<llvm::AllocaInst>(Inst)) {755 Inst++;756 if (!isa<ConstantData>(AllocaInst->getArraySize()))757 continue;758 AllocaInst->moveBeforePreserving(MoveLocInst);759 } else {760 Inst++;761 }762 }763 }764}765 766void OpenMPIRBuilder::finalize(Function *Fn) {767 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;768 SmallVector<BasicBlock *, 32> Blocks;769 SmallVector<OutlineInfo, 16> DeferredOutlines;770 for (OutlineInfo &OI : OutlineInfos) {771 // Skip functions that have not finalized yet; may happen with nested772 // function generation.773 if (Fn && OI.getFunction() != Fn) {774 DeferredOutlines.push_back(OI);775 continue;776 }777 778 ParallelRegionBlockSet.clear();779 Blocks.clear();780 OI.collectBlocks(ParallelRegionBlockSet, Blocks);781 782 Function *OuterFn = OI.getFunction();783 CodeExtractorAnalysisCache CEAC(*OuterFn);784 // If we generate code for the target device, we need to allocate785 // struct for aggregate params in the device default alloca address space.786 // OpenMP runtime requires that the params of the extracted functions are787 // passed as zero address space pointers. This flag ensures that788 // CodeExtractor generates correct code for extracted functions789 // which are used by OpenMP runtime.790 bool ArgsInZeroAddressSpace = Config.isTargetDevice();791 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,792 /* AggregateArgs */ true,793 /* BlockFrequencyInfo */ nullptr,794 /* BranchProbabilityInfo */ nullptr,795 /* AssumptionCache */ nullptr,796 /* AllowVarArgs */ true,797 /* AllowAlloca */ true,798 /* AllocaBlock*/ OI.OuterAllocaBB,799 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);800 801 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");802 LLVM_DEBUG(dbgs() << "Entry " << OI.EntryBB->getName()803 << " Exit: " << OI.ExitBB->getName() << "\n");804 assert(Extractor.isEligible() &&805 "Expected OpenMP outlining to be possible!");806 807 for (auto *V : OI.ExcludeArgsFromAggregate)808 Extractor.excludeArgFromAggregate(V);809 810 Function *OutlinedFn = Extractor.extractCodeRegion(CEAC);811 812 // Forward target-cpu, target-features attributes to the outlined function.813 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");814 if (TargetCpuAttr.isStringAttribute())815 OutlinedFn->addFnAttr(TargetCpuAttr);816 817 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");818 if (TargetFeaturesAttr.isStringAttribute())819 OutlinedFn->addFnAttr(TargetFeaturesAttr);820 821 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");822 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");823 assert(OutlinedFn->getReturnType()->isVoidTy() &&824 "OpenMP outlined functions should not return a value!");825 826 // For compability with the clang CG we move the outlined function after the827 // one with the parallel region.828 OutlinedFn->removeFromParent();829 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);830 831 // Remove the artificial entry introduced by the extractor right away, we832 // made our own entry block after all.833 {834 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();835 assert(ArtificialEntry.getUniqueSuccessor() == OI.EntryBB);836 assert(OI.EntryBB->getUniquePredecessor() == &ArtificialEntry);837 // Move instructions from the to-be-deleted ArtificialEntry to the entry838 // basic block of the parallel region. CodeExtractor generates839 // instructions to unwrap the aggregate argument and may sink840 // allocas/bitcasts for values that are solely used in the outlined region841 // and do not escape.842 assert(!ArtificialEntry.empty() &&843 "Expected instructions to add in the outlined region entry");844 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),845 End = ArtificialEntry.rend();846 It != End;) {847 Instruction &I = *It;848 It++;849 850 if (I.isTerminator()) {851 // Absorb any debug value that terminator may have852 if (OI.EntryBB->getTerminator())853 OI.EntryBB->getTerminator()->adoptDbgRecords(854 &ArtificialEntry, I.getIterator(), false);855 continue;856 }857 858 I.moveBeforePreserving(*OI.EntryBB, OI.EntryBB->getFirstInsertionPt());859 }860 861 OI.EntryBB->moveBefore(&ArtificialEntry);862 ArtificialEntry.eraseFromParent();863 }864 assert(&OutlinedFn->getEntryBlock() == OI.EntryBB);865 assert(OutlinedFn && OutlinedFn->hasNUses(1));866 867 // Run a user callback, e.g. to add attributes.868 if (OI.PostOutlineCB)869 OI.PostOutlineCB(*OutlinedFn);870 }871 872 // Remove work items that have been completed.873 OutlineInfos = std::move(DeferredOutlines);874 875 // The createTarget functions embeds user written code into876 // the target region which may inject allocas which need to877 // be moved to the entry block of our target or risk malformed878 // optimisations by later passes, this is only relevant for879 // the device pass which appears to be a little more delicate880 // when it comes to optimisations (however, we do not block on881 // that here, it's up to the inserter to the list to do so).882 // This notbaly has to occur after the OutlinedInfo candidates883 // have been extracted so we have an end product that will not884 // be implicitly adversely affected by any raises unless885 // intentionally appended to the list.886 // NOTE: This only does so for ConstantData, it could be extended887 // to ConstantExpr's with further effort, however, they should888 // largely be folded when they get here. Extending it to runtime889 // defined/read+writeable allocation sizes would be non-trivial890 // (need to factor in movement of any stores to variables the891 // allocation size depends on, as well as the usual loads,892 // otherwise it'll yield the wrong result after movement) and893 // likely be more suitable as an LLVM optimisation pass.894 for (Function *F : ConstantAllocaRaiseCandidates)895 raiseUserConstantDataAllocasToEntryBlock(Builder, F);896 897 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =898 [](EmitMetadataErrorKind Kind,899 const TargetRegionEntryInfo &EntryInfo) -> void {900 errs() << "Error of kind: " << Kind901 << " when emitting offload entries and metadata during "902 "OMPIRBuilder finalization \n";903 };904 905 if (!OffloadInfoManager.empty())906 createOffloadEntriesAndInfoMetadata(ErrorReportFn);907 908 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {909 std::vector<WeakTrackingVH> LLVMCompilerUsed = {910 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};911 emitUsed("llvm.compiler.used", LLVMCompilerUsed);912 }913 914 IsFinalized = true;915}916 917bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }918 919OpenMPIRBuilder::~OpenMPIRBuilder() {920 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");921}922 923GlobalValue *OpenMPIRBuilder::createGlobalFlag(unsigned Value, StringRef Name) {924 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());925 auto *GV =926 new GlobalVariable(M, I32Ty,927 /* isConstant = */ true, GlobalValue::WeakODRLinkage,928 ConstantInt::get(I32Ty, Value), Name);929 GV->setVisibility(GlobalValue::HiddenVisibility);930 931 return GV;932}933 934void OpenMPIRBuilder::emitUsed(StringRef Name, ArrayRef<WeakTrackingVH> List) {935 if (List.empty())936 return;937 938 // Convert List to what ConstantArray needs.939 SmallVector<Constant *, 8> UsedArray;940 UsedArray.resize(List.size());941 for (unsigned I = 0, E = List.size(); I != E; ++I)942 UsedArray[I] = ConstantExpr::getPointerBitCastOrAddrSpaceCast(943 cast<Constant>(&*List[I]), Builder.getPtrTy());944 945 if (UsedArray.empty())946 return;947 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());948 949 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,950 ConstantArray::get(ATy, UsedArray), Name);951 952 GV->setSection("llvm.metadata");953}954 955GlobalVariable *956OpenMPIRBuilder::emitKernelExecutionMode(StringRef KernelName,957 OMPTgtExecModeFlags Mode) {958 auto *Int8Ty = Builder.getInt8Ty();959 auto *GVMode = new GlobalVariable(960 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,961 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));962 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);963 return GVMode;964}965 966Constant *OpenMPIRBuilder::getOrCreateIdent(Constant *SrcLocStr,967 uint32_t SrcLocStrSize,968 IdentFlag LocFlags,969 unsigned Reserve2Flags) {970 // Enable "C-mode".971 LocFlags |= OMP_IDENT_FLAG_KMPC;972 973 Constant *&Ident =974 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];975 if (!Ident) {976 Constant *I32Null = ConstantInt::getNullValue(Int32);977 Constant *IdentData[] = {I32Null,978 ConstantInt::get(Int32, uint32_t(LocFlags)),979 ConstantInt::get(Int32, Reserve2Flags),980 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};981 982 size_t SrcLocStrArgIdx = 4;983 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)984 ->getPointerAddressSpace() !=985 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())986 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(987 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));988 Constant *Initializer =989 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);990 991 // Look for existing encoding of the location + flags, not needed but992 // minimizes the difference to the existing solution while we transition.993 for (GlobalVariable &GV : M.globals())994 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())995 if (GV.getInitializer() == Initializer)996 Ident = &GV;997 998 if (!Ident) {999 auto *GV = new GlobalVariable(1000 M, OpenMPIRBuilder::Ident,1001 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",1002 nullptr, GlobalValue::NotThreadLocal,1003 M.getDataLayout().getDefaultGlobalsAddressSpace());1004 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);1005 GV->setAlignment(Align(8));1006 Ident = GV;1007 }1008 }1009 1010 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);1011}1012 1013Constant *OpenMPIRBuilder::getOrCreateSrcLocStr(StringRef LocStr,1014 uint32_t &SrcLocStrSize) {1015 SrcLocStrSize = LocStr.size();1016 Constant *&SrcLocStr = SrcLocStrMap[LocStr];1017 if (!SrcLocStr) {1018 Constant *Initializer =1019 ConstantDataArray::getString(M.getContext(), LocStr);1020 1021 // Look for existing encoding of the location, not needed but minimizes the1022 // difference to the existing solution while we transition.1023 for (GlobalVariable &GV : M.globals())1024 if (GV.isConstant() && GV.hasInitializer() &&1025 GV.getInitializer() == Initializer)1026 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);1027 1028 SrcLocStr = Builder.CreateGlobalString(1029 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),1030 &M);1031 }1032 return SrcLocStr;1033}1034 1035Constant *OpenMPIRBuilder::getOrCreateSrcLocStr(StringRef FunctionName,1036 StringRef FileName,1037 unsigned Line, unsigned Column,1038 uint32_t &SrcLocStrSize) {1039 SmallString<128> Buffer;1040 Buffer.push_back(';');1041 Buffer.append(FileName);1042 Buffer.push_back(';');1043 Buffer.append(FunctionName);1044 Buffer.push_back(';');1045 Buffer.append(std::to_string(Line));1046 Buffer.push_back(';');1047 Buffer.append(std::to_string(Column));1048 Buffer.push_back(';');1049 Buffer.push_back(';');1050 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);1051}1052 1053Constant *1054OpenMPIRBuilder::getOrCreateDefaultSrcLocStr(uint32_t &SrcLocStrSize) {1055 StringRef UnknownLoc = ";unknown;unknown;0;0;;";1056 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);1057}1058 1059Constant *OpenMPIRBuilder::getOrCreateSrcLocStr(DebugLoc DL,1060 uint32_t &SrcLocStrSize,1061 Function *F) {1062 DILocation *DIL = DL.get();1063 if (!DIL)1064 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);1065 StringRef FileName = M.getName();1066 if (DIFile *DIF = DIL->getFile())1067 if (std::optional<StringRef> Source = DIF->getSource())1068 FileName = *Source;1069 StringRef Function = DIL->getScope()->getSubprogram()->getName();1070 if (Function.empty() && F)1071 Function = F->getName();1072 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),1073 DIL->getColumn(), SrcLocStrSize);1074}1075 1076Constant *OpenMPIRBuilder::getOrCreateSrcLocStr(const LocationDescription &Loc,1077 uint32_t &SrcLocStrSize) {1078 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,1079 Loc.IP.getBlock()->getParent());1080}1081 1082Value *OpenMPIRBuilder::getOrCreateThreadID(Value *Ident) {1083 return createRuntimeFunctionCall(1084 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,1085 "omp_global_thread_num");1086}1087 1088OpenMPIRBuilder::InsertPointOrErrorTy1089OpenMPIRBuilder::createBarrier(const LocationDescription &Loc, Directive Kind,1090 bool ForceSimpleCall, bool CheckCancelFlag) {1091 if (!updateToLocation(Loc))1092 return Loc.IP;1093 1094 // Build call __kmpc_cancel_barrier(loc, thread_id) or1095 // __kmpc_barrier(loc, thread_id);1096 1097 IdentFlag BarrierLocFlags;1098 switch (Kind) {1099 case OMPD_for:1100 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;1101 break;1102 case OMPD_sections:1103 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;1104 break;1105 case OMPD_single:1106 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;1107 break;1108 case OMPD_barrier:1109 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;1110 break;1111 default:1112 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;1113 break;1114 }1115 1116 uint32_t SrcLocStrSize;1117 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);1118 Value *Args[] = {1119 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),1120 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};1121 1122 // If we are in a cancellable parallel region, barriers are cancellation1123 // points.1124 // TODO: Check why we would force simple calls or to ignore the cancel flag.1125 bool UseCancelBarrier =1126 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);1127 1128 Value *Result = createRuntimeFunctionCall(1129 getOrCreateRuntimeFunctionPtr(UseCancelBarrier1130 ? OMPRTL___kmpc_cancel_barrier1131 : OMPRTL___kmpc_barrier),1132 Args);1133 1134 if (UseCancelBarrier && CheckCancelFlag)1135 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))1136 return Err;1137 1138 return Builder.saveIP();1139}1140 1141OpenMPIRBuilder::InsertPointOrErrorTy1142OpenMPIRBuilder::createCancel(const LocationDescription &Loc,1143 Value *IfCondition,1144 omp::Directive CanceledDirective) {1145 if (!updateToLocation(Loc))1146 return Loc.IP;1147 1148 // LLVM utilities like blocks with terminators.1149 auto *UI = Builder.CreateUnreachable();1150 1151 Instruction *ThenTI = UI, *ElseTI = nullptr;1152 if (IfCondition) {1153 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);1154 1155 // Even if the if condition evaluates to false, this should count as a1156 // cancellation point1157 Builder.SetInsertPoint(ElseTI);1158 auto ElseIP = Builder.saveIP();1159 1160 InsertPointOrErrorTy IPOrErr = createCancellationPoint(1161 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);1162 if (!IPOrErr)1163 return IPOrErr;1164 }1165 1166 Builder.SetInsertPoint(ThenTI);1167 1168 Value *CancelKind = nullptr;1169 switch (CanceledDirective) {1170#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \1171 case DirectiveEnum: \1172 CancelKind = Builder.getInt32(Value); \1173 break;1174#include "llvm/Frontend/OpenMP/OMPKinds.def"1175 default:1176 llvm_unreachable("Unknown cancel kind!");1177 }1178 1179 uint32_t SrcLocStrSize;1180 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);1181 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);1182 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};1183 Value *Result = createRuntimeFunctionCall(1184 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);1185 1186 // The actual cancel logic is shared with others, e.g., cancel_barriers.1187 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))1188 return Err;1189 1190 // Update the insertion point and remove the terminator we introduced.1191 Builder.SetInsertPoint(UI->getParent());1192 UI->eraseFromParent();1193 1194 return Builder.saveIP();1195}1196 1197OpenMPIRBuilder::InsertPointOrErrorTy1198OpenMPIRBuilder::createCancellationPoint(const LocationDescription &Loc,1199 omp::Directive CanceledDirective) {1200 if (!updateToLocation(Loc))1201 return Loc.IP;1202 1203 // LLVM utilities like blocks with terminators.1204 auto *UI = Builder.CreateUnreachable();1205 Builder.SetInsertPoint(UI);1206 1207 Value *CancelKind = nullptr;1208 switch (CanceledDirective) {1209#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \1210 case DirectiveEnum: \1211 CancelKind = Builder.getInt32(Value); \1212 break;1213#include "llvm/Frontend/OpenMP/OMPKinds.def"1214 default:1215 llvm_unreachable("Unknown cancel kind!");1216 }1217 1218 uint32_t SrcLocStrSize;1219 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);1220 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);1221 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};1222 Value *Result = createRuntimeFunctionCall(1223 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);1224 1225 // The actual cancel logic is shared with others, e.g., cancel_barriers.1226 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))1227 return Err;1228 1229 // Update the insertion point and remove the terminator we introduced.1230 Builder.SetInsertPoint(UI->getParent());1231 UI->eraseFromParent();1232 1233 return Builder.saveIP();1234}1235 1236OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitTargetKernel(1237 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,1238 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,1239 Value *HostPtr, ArrayRef<Value *> KernelArgs) {1240 if (!updateToLocation(Loc))1241 return Loc.IP;1242 1243 Builder.restoreIP(AllocaIP);1244 auto *KernelArgsPtr =1245 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");1246 updateToLocation(Loc);1247 1248 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {1249 llvm::Value *Arg =1250 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);1251 Builder.CreateAlignedStore(1252 KernelArgs[I], Arg,1253 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));1254 }1255 1256 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,1257 NumThreads, HostPtr, KernelArgsPtr};1258 1259 Return = createRuntimeFunctionCall(1260 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),1261 OffloadingArgs);1262 1263 return Builder.saveIP();1264}1265 1266OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitKernelLaunch(1267 const LocationDescription &Loc, Value *OutlinedFnID,1268 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,1269 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {1270 1271 if (!updateToLocation(Loc))1272 return Loc.IP;1273 1274 // On top of the arrays that were filled up, the target offloading call1275 // takes as arguments the device id as well as the host pointer. The host1276 // pointer is used by the runtime library to identify the current target1277 // region, so it only has to be unique and not necessarily point to1278 // anything. It could be the pointer to the outlined function that1279 // implements the target region, but we aren't using that so that the1280 // compiler doesn't need to keep that, and could therefore inline the host1281 // function if proven worthwhile during optimization.1282 1283 // From this point on, we need to have an ID of the target region defined.1284 assert(OutlinedFnID && "Invalid outlined function ID!");1285 (void)OutlinedFnID;1286 1287 // Return value of the runtime offloading call.1288 Value *Return = nullptr;1289 1290 // Arguments for the target kernel.1291 SmallVector<Value *> ArgsVector;1292 getKernelArgsVector(Args, Builder, ArgsVector);1293 1294 // The target region is an outlined function launched by the runtime1295 // via calls to __tgt_target_kernel().1296 //1297 // Note that on the host and CPU targets, the runtime implementation of1298 // these calls simply call the outlined function without forking threads.1299 // The outlined functions themselves have runtime calls to1300 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by1301 // the compiler in emitTeamsCall() and emitParallelCall().1302 //1303 // In contrast, on the NVPTX target, the implementation of1304 // __tgt_target_teams() launches a GPU kernel with the requested number1305 // of teams and threads so no additional calls to the runtime are required.1306 // Check the error code and execute the host version if required.1307 Builder.restoreIP(emitTargetKernel(1308 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),1309 Args.NumThreads.front(), OutlinedFnID, ArgsVector));1310 1311 BasicBlock *OffloadFailedBlock =1312 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");1313 BasicBlock *OffloadContBlock =1314 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");1315 Value *Failed = Builder.CreateIsNotNull(Return);1316 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);1317 1318 auto CurFn = Builder.GetInsertBlock()->getParent();1319 emitBlock(OffloadFailedBlock, CurFn);1320 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());1321 if (!AfterIP)1322 return AfterIP.takeError();1323 Builder.restoreIP(*AfterIP);1324 emitBranch(OffloadContBlock);1325 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);1326 return Builder.saveIP();1327}1328 1329Error OpenMPIRBuilder::emitCancelationCheckImpl(1330 Value *CancelFlag, omp::Directive CanceledDirective) {1331 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&1332 "Unexpected cancellation!");1333 1334 // For a cancel barrier we create two new blocks.1335 BasicBlock *BB = Builder.GetInsertBlock();1336 BasicBlock *NonCancellationBlock;1337 if (Builder.GetInsertPoint() == BB->end()) {1338 // TODO: This branch will not be needed once we moved to the1339 // OpenMPIRBuilder codegen completely.1340 NonCancellationBlock = BasicBlock::Create(1341 BB->getContext(), BB->getName() + ".cont", BB->getParent());1342 } else {1343 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());1344 BB->getTerminator()->eraseFromParent();1345 Builder.SetInsertPoint(BB);1346 }1347 BasicBlock *CancellationBlock = BasicBlock::Create(1348 BB->getContext(), BB->getName() + ".cncl", BB->getParent());1349 1350 // Jump to them based on the return value.1351 Value *Cmp = Builder.CreateIsNull(CancelFlag);1352 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,1353 /* TODO weight */ nullptr, nullptr);1354 1355 // From the cancellation block we finalize all variables and go to the1356 // post finalization block that is known to the FiniCB callback.1357 auto &FI = FinalizationStack.back();1358 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);1359 if (!FiniBBOrErr)1360 return FiniBBOrErr.takeError();1361 Builder.SetInsertPoint(CancellationBlock);1362 Builder.CreateBr(*FiniBBOrErr);1363 1364 // The continuation block is where code generation continues.1365 Builder.SetInsertPoint(NonCancellationBlock, NonCancellationBlock->begin());1366 return Error::success();1367}1368 1369// Callback used to create OpenMP runtime calls to support1370// omp parallel clause for the device.1371// We need to use this callback to replace call to the OutlinedFn in OuterFn1372// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_51)1373static void targetParallelCallback(1374 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,1375 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,1376 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,1377 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {1378 // Add some known attributes.1379 IRBuilder<> &Builder = OMPIRBuilder->Builder;1380 OutlinedFn.addParamAttr(0, Attribute::NoAlias);1381 OutlinedFn.addParamAttr(1, Attribute::NoAlias);1382 OutlinedFn.addParamAttr(0, Attribute::NoUndef);1383 OutlinedFn.addParamAttr(1, Attribute::NoUndef);1384 OutlinedFn.addFnAttr(Attribute::NoUnwind);1385 1386 assert(OutlinedFn.arg_size() >= 2 &&1387 "Expected at least tid and bounded tid as arguments");1388 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;1389 1390 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());1391 assert(CI && "Expected call instruction to outlined function");1392 CI->getParent()->setName("omp_parallel");1393 1394 Builder.SetInsertPoint(CI);1395 Type *PtrTy = OMPIRBuilder->VoidPtr;1396 Value *NullPtrValue = Constant::getNullValue(PtrTy);1397 1398 // Add alloca for kernel args1399 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();1400 Builder.SetInsertPoint(OuterAllocaBB, OuterAllocaBB->getFirstInsertionPt());1401 AllocaInst *ArgsAlloca =1402 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));1403 Value *Args = ArgsAlloca;1404 // Add address space cast if array for storing arguments is not allocated1405 // in address space 01406 if (ArgsAlloca->getAddressSpace())1407 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);1408 Builder.restoreIP(CurrentIP);1409 1410 // Store captured vars which are used by kmpc_parallel_511411 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {1412 Value *V = *(CI->arg_begin() + 2 + Idx);1413 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(1414 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);1415 Builder.CreateStore(V, StoreAddress);1416 }1417 1418 Value *Cond =1419 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)1420 : Builder.getInt32(1);1421 1422 // Build kmpc_parallel_51 call1423 Value *Parallel51CallArgs[] = {1424 /* identifier*/ Ident,1425 /* global thread num*/ ThreadID,1426 /* if expression */ Cond,1427 /* number of threads */ NumThreads ? NumThreads : Builder.getInt32(-1),1428 /* Proc bind */ Builder.getInt32(-1),1429 /* outlined function */ &OutlinedFn,1430 /* wrapper function */ NullPtrValue,1431 /* arguments of the outlined funciton*/ Args,1432 /* number of arguments */ Builder.getInt64(NumCapturedVars)};1433 1434 FunctionCallee RTLFn =1435 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_51);1436 1437 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel51CallArgs);1438 1439 LLVM_DEBUG(dbgs() << "With kmpc_parallel_51 placed: "1440 << *Builder.GetInsertBlock()->getParent() << "\n");1441 1442 // Initialize the local TID stack location with the argument value.1443 Builder.SetInsertPoint(PrivTID);1444 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();1445 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),1446 PrivTIDAddr);1447 1448 // Remove redundant call to the outlined function.1449 CI->eraseFromParent();1450 1451 for (Instruction *I : ToBeDeleted) {1452 I->eraseFromParent();1453 }1454}1455 1456// Callback used to create OpenMP runtime calls to support1457// omp parallel clause for the host.1458// We need to use this callback to replace call to the OutlinedFn in OuterFn1459// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])1460static void1461hostParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn,1462 Function *OuterFn, Value *Ident, Value *IfCondition,1463 Instruction *PrivTID, AllocaInst *PrivTIDAddr,1464 const SmallVector<Instruction *, 4> &ToBeDeleted) {1465 IRBuilder<> &Builder = OMPIRBuilder->Builder;1466 FunctionCallee RTLFn;1467 if (IfCondition) {1468 RTLFn =1469 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);1470 } else {1471 RTLFn =1472 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);1473 }1474 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {1475 if (!F->hasMetadata(LLVMContext::MD_callback)) {1476 LLVMContext &Ctx = F->getContext();1477 MDBuilder MDB(Ctx);1478 // Annotate the callback behavior of the __kmpc_fork_call:1479 // - The callback callee is argument number 2 (microtask).1480 // - The first two arguments of the callback callee are unknown (-1).1481 // - All variadic arguments to the __kmpc_fork_call are passed to the1482 // callback callee.1483 F->addMetadata(LLVMContext::MD_callback,1484 *MDNode::get(Ctx, {MDB.createCallbackEncoding(1485 2, {-1, -1},1486 /* VarArgsArePassed */ true)}));1487 }1488 }1489 // Add some known attributes.1490 OutlinedFn.addParamAttr(0, Attribute::NoAlias);1491 OutlinedFn.addParamAttr(1, Attribute::NoAlias);1492 OutlinedFn.addFnAttr(Attribute::NoUnwind);1493 1494 assert(OutlinedFn.arg_size() >= 2 &&1495 "Expected at least tid and bounded tid as arguments");1496 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;1497 1498 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());1499 CI->getParent()->setName("omp_parallel");1500 Builder.SetInsertPoint(CI);1501 1502 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);1503 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),1504 &OutlinedFn};1505 1506 SmallVector<Value *, 16> RealArgs;1507 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));1508 if (IfCondition) {1509 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);1510 RealArgs.push_back(Cond);1511 }1512 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());1513 1514 // __kmpc_fork_call_if always expects a void ptr as the last argument1515 // If there are no arguments, pass a null pointer.1516 auto PtrTy = OMPIRBuilder->VoidPtr;1517 if (IfCondition && NumCapturedVars == 0) {1518 Value *NullPtrValue = Constant::getNullValue(PtrTy);1519 RealArgs.push_back(NullPtrValue);1520 }1521 1522 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);1523 1524 LLVM_DEBUG(dbgs() << "With fork_call placed: "1525 << *Builder.GetInsertBlock()->getParent() << "\n");1526 1527 // Initialize the local TID stack location with the argument value.1528 Builder.SetInsertPoint(PrivTID);1529 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();1530 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),1531 PrivTIDAddr);1532 1533 // Remove redundant call to the outlined function.1534 CI->eraseFromParent();1535 1536 for (Instruction *I : ToBeDeleted) {1537 I->eraseFromParent();1538 }1539}1540 1541OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createParallel(1542 const LocationDescription &Loc, InsertPointTy OuterAllocaIP,1543 BodyGenCallbackTy BodyGenCB, PrivatizeCallbackTy PrivCB,1544 FinalizeCallbackTy FiniCB, Value *IfCondition, Value *NumThreads,1545 omp::ProcBindKind ProcBind, bool IsCancellable) {1546 assert(!isConflictIP(Loc.IP, OuterAllocaIP) && "IPs must not be ambiguous");1547 1548 if (!updateToLocation(Loc))1549 return Loc.IP;1550 1551 uint32_t SrcLocStrSize;1552 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);1553 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);1554 Value *ThreadID = getOrCreateThreadID(Ident);1555 // If we generate code for the target device, we need to allocate1556 // struct for aggregate params in the device default alloca address space.1557 // OpenMP runtime requires that the params of the extracted functions are1558 // passed as zero address space pointers. This flag ensures that extracted1559 // function arguments are declared in zero address space1560 bool ArgsInZeroAddressSpace = Config.isTargetDevice();1561 1562 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)1563 // only if we compile for host side.1564 if (NumThreads && !Config.isTargetDevice()) {1565 Value *Args[] = {1566 Ident, ThreadID,1567 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};1568 createRuntimeFunctionCall(1569 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);1570 }1571 1572 if (ProcBind != OMP_PROC_BIND_default) {1573 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)1574 Value *Args[] = {1575 Ident, ThreadID,1576 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};1577 createRuntimeFunctionCall(1578 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);1579 }1580 1581 BasicBlock *InsertBB = Builder.GetInsertBlock();1582 Function *OuterFn = InsertBB->getParent();1583 1584 // Save the outer alloca block because the insertion iterator may get1585 // invalidated and we still need this later.1586 BasicBlock *OuterAllocaBlock = OuterAllocaIP.getBlock();1587 1588 // Vector to remember instructions we used only during the modeling but which1589 // we want to delete at the end.1590 SmallVector<Instruction *, 4> ToBeDeleted;1591 1592 // Change the location to the outer alloca insertion point to create and1593 // initialize the allocas we pass into the parallel region.1594 InsertPointTy NewOuter(OuterAllocaBlock, OuterAllocaBlock->begin());1595 Builder.restoreIP(NewOuter);1596 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");1597 AllocaInst *ZeroAddrAlloca =1598 Builder.CreateAlloca(Int32, nullptr, "zero.addr");1599 Instruction *TIDAddr = TIDAddrAlloca;1600 Instruction *ZeroAddr = ZeroAddrAlloca;1601 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {1602 // Add additional casts to enforce pointers in zero address space1603 TIDAddr = new AddrSpaceCastInst(1604 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");1605 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());1606 ToBeDeleted.push_back(TIDAddr);1607 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,1608 PointerType ::get(M.getContext(), 0),1609 "zero.addr.ascast");1610 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());1611 ToBeDeleted.push_back(ZeroAddr);1612 }1613 1614 // We only need TIDAddr and ZeroAddr for modeling purposes to get the1615 // associated arguments in the outlined function, so we delete them later.1616 ToBeDeleted.push_back(TIDAddrAlloca);1617 ToBeDeleted.push_back(ZeroAddrAlloca);1618 1619 // Create an artificial insertion point that will also ensure the blocks we1620 // are about to split are not degenerated.1621 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);1622 1623 BasicBlock *EntryBB = UI->getParent();1624 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");1625 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");1626 BasicBlock *PRegPreFiniBB =1627 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");1628 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");1629 1630 auto FiniCBWrapper = [&](InsertPointTy IP) {1631 // Hide "open-ended" blocks from the given FiniCB by setting the right jump1632 // target to the region exit block.1633 if (IP.getBlock()->end() == IP.getPoint()) {1634 IRBuilder<>::InsertPointGuard IPG(Builder);1635 Builder.restoreIP(IP);1636 Instruction *I = Builder.CreateBr(PRegExitBB);1637 IP = InsertPointTy(I->getParent(), I->getIterator());1638 }1639 assert(IP.getBlock()->getTerminator()->getNumSuccessors() == 1 &&1640 IP.getBlock()->getTerminator()->getSuccessor(0) == PRegExitBB &&1641 "Unexpected insertion point for finalization call!");1642 return FiniCB(IP);1643 };1644 1645 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});1646 1647 // Generate the privatization allocas in the block that will become the entry1648 // of the outlined function.1649 Builder.SetInsertPoint(PRegEntryBB->getTerminator());1650 InsertPointTy InnerAllocaIP = Builder.saveIP();1651 1652 AllocaInst *PrivTIDAddr =1653 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");1654 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");1655 1656 // Add some fake uses for OpenMP provided arguments.1657 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));1658 Instruction *ZeroAddrUse =1659 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");1660 ToBeDeleted.push_back(ZeroAddrUse);1661 1662 // EntryBB1663 // |1664 // V1665 // PRegionEntryBB <- Privatization allocas are placed here.1666 // |1667 // V1668 // PRegionBodyBB <- BodeGen is invoked here.1669 // |1670 // V1671 // PRegPreFiniBB <- The block we will start finalization from.1672 // |1673 // V1674 // PRegionExitBB <- A common exit to simplify block collection.1675 //1676 1677 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");1678 1679 // Let the caller create the body.1680 assert(BodyGenCB && "Expected body generation callback!");1681 InsertPointTy CodeGenIP(PRegBodyBB, PRegBodyBB->begin());1682 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP))1683 return Err;1684 1685 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");1686 1687 OutlineInfo OI;1688 if (Config.isTargetDevice()) {1689 // Generate OpenMP target specific runtime call1690 OI.PostOutlineCB = [=, ToBeDeletedVec =1691 std::move(ToBeDeleted)](Function &OutlinedFn) {1692 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,1693 IfCondition, NumThreads, PrivTID, PrivTIDAddr,1694 ThreadID, ToBeDeletedVec);1695 };1696 } else {1697 // Generate OpenMP host runtime call1698 OI.PostOutlineCB = [=, ToBeDeletedVec =1699 std::move(ToBeDeleted)](Function &OutlinedFn) {1700 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,1701 PrivTID, PrivTIDAddr, ToBeDeletedVec);1702 };1703 }1704 1705 OI.OuterAllocaBB = OuterAllocaBlock;1706 OI.EntryBB = PRegEntryBB;1707 OI.ExitBB = PRegExitBB;1708 1709 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;1710 SmallVector<BasicBlock *, 32> Blocks;1711 OI.collectBlocks(ParallelRegionBlockSet, Blocks);1712 1713 CodeExtractorAnalysisCache CEAC(*OuterFn);1714 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,1715 /* AggregateArgs */ false,1716 /* BlockFrequencyInfo */ nullptr,1717 /* BranchProbabilityInfo */ nullptr,1718 /* AssumptionCache */ nullptr,1719 /* AllowVarArgs */ true,1720 /* AllowAlloca */ true,1721 /* AllocationBlock */ OuterAllocaBlock,1722 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);1723 1724 // Find inputs to, outputs from the code region.1725 BasicBlock *CommonExit = nullptr;1726 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;1727 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);1728 1729 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,1730 /*CollectGlobalInputs=*/true);1731 1732 Inputs.remove_if([&](Value *I) {1733 if (auto *GV = dyn_cast_if_present<GlobalVariable>(I))1734 return GV->getValueType() == OpenMPIRBuilder::Ident;1735 1736 return false;1737 });1738 1739 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");1740 1741 FunctionCallee TIDRTLFn =1742 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);1743 1744 auto PrivHelper = [&](Value &V) -> Error {1745 if (&V == TIDAddr || &V == ZeroAddr) {1746 OI.ExcludeArgsFromAggregate.push_back(&V);1747 return Error::success();1748 }1749 1750 SetVector<Use *> Uses;1751 for (Use &U : V.uses())1752 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))1753 if (ParallelRegionBlockSet.count(UserI->getParent()))1754 Uses.insert(&U);1755 1756 // __kmpc_fork_call expects extra arguments as pointers. If the input1757 // already has a pointer type, everything is fine. Otherwise, store the1758 // value onto stack and load it back inside the to-be-outlined region. This1759 // will ensure only the pointer will be passed to the function.1760 // FIXME: if there are more than 15 trailing arguments, they must be1761 // additionally packed in a struct.1762 Value *Inner = &V;1763 if (!V.getType()->isPointerTy()) {1764 IRBuilder<>::InsertPointGuard Guard(Builder);1765 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");1766 1767 Builder.restoreIP(OuterAllocaIP);1768 Value *Ptr =1769 Builder.CreateAlloca(V.getType(), nullptr, V.getName() + ".reloaded");1770 1771 // Store to stack at end of the block that currently branches to the entry1772 // block of the to-be-outlined region.1773 Builder.SetInsertPoint(InsertBB,1774 InsertBB->getTerminator()->getIterator());1775 Builder.CreateStore(&V, Ptr);1776 1777 // Load back next to allocations in the to-be-outlined region.1778 Builder.restoreIP(InnerAllocaIP);1779 Inner = Builder.CreateLoad(V.getType(), Ptr);1780 }1781 1782 Value *ReplacementValue = nullptr;1783 CallInst *CI = dyn_cast<CallInst>(&V);1784 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {1785 ReplacementValue = PrivTID;1786 } else {1787 InsertPointOrErrorTy AfterIP =1788 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);1789 if (!AfterIP)1790 return AfterIP.takeError();1791 Builder.restoreIP(*AfterIP);1792 InnerAllocaIP = {1793 InnerAllocaIP.getBlock(),1794 InnerAllocaIP.getBlock()->getTerminator()->getIterator()};1795 1796 assert(ReplacementValue &&1797 "Expected copy/create callback to set replacement value!");1798 if (ReplacementValue == &V)1799 return Error::success();1800 }1801 1802 for (Use *UPtr : Uses)1803 UPtr->set(ReplacementValue);1804 1805 return Error::success();1806 };1807 1808 // Reset the inner alloca insertion as it will be used for loading the values1809 // wrapped into pointers before passing them into the to-be-outlined region.1810 // Configure it to insert immediately after the fake use of zero address so1811 // that they are available in the generated body and so that the1812 // OpenMP-related values (thread ID and zero address pointers) remain leading1813 // in the argument list.1814 InnerAllocaIP = IRBuilder<>::InsertPoint(1815 ZeroAddrUse->getParent(), ZeroAddrUse->getNextNode()->getIterator());1816 1817 // Reset the outer alloca insertion point to the entry of the relevant block1818 // in case it was invalidated.1819 OuterAllocaIP = IRBuilder<>::InsertPoint(1820 OuterAllocaBlock, OuterAllocaBlock->getFirstInsertionPt());1821 1822 for (Value *Input : Inputs) {1823 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");1824 if (Error Err = PrivHelper(*Input))1825 return Err;1826 }1827 LLVM_DEBUG({1828 for (Value *Output : Outputs)1829 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");1830 });1831 assert(Outputs.empty() &&1832 "OpenMP outlining should not produce live-out values!");1833 1834 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");1835 LLVM_DEBUG({1836 for (auto *BB : Blocks)1837 dbgs() << " PBR: " << BB->getName() << "\n";1838 });1839 1840 // Adjust the finalization stack, verify the adjustment, and call the1841 // finalize function a last time to finalize values between the pre-fini1842 // block and the exit block if we left the parallel "the normal way".1843 auto FiniInfo = FinalizationStack.pop_back_val();1844 (void)FiniInfo;1845 assert(FiniInfo.DK == OMPD_parallel &&1846 "Unexpected finalization stack state!");1847 1848 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();1849 1850 InsertPointTy PreFiniIP(PRegPreFiniBB, PRegPreFiniTI->getIterator());1851 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);1852 if (!FiniBBOrErr)1853 return FiniBBOrErr.takeError();1854 {1855 IRBuilderBase::InsertPointGuard Guard(Builder);1856 Builder.restoreIP(PreFiniIP);1857 Builder.CreateBr(*FiniBBOrErr);1858 // There's currently a branch to omp.par.exit. Delete it. We will get there1859 // via the fini block1860 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())1861 Term->eraseFromParent();1862 }1863 1864 // Register the outlined info.1865 addOutlineInfo(std::move(OI));1866 1867 InsertPointTy AfterIP(UI->getParent(), UI->getParent()->end());1868 UI->eraseFromParent();1869 1870 return AfterIP;1871}1872 1873void OpenMPIRBuilder::emitFlush(const LocationDescription &Loc) {1874 // Build call void __kmpc_flush(ident_t *loc)1875 uint32_t SrcLocStrSize;1876 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);1877 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};1878 1879 createRuntimeFunctionCall(getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_flush),1880 Args);1881}1882 1883void OpenMPIRBuilder::createFlush(const LocationDescription &Loc) {1884 if (!updateToLocation(Loc))1885 return;1886 emitFlush(Loc);1887}1888 1889void OpenMPIRBuilder::emitTaskwaitImpl(const LocationDescription &Loc) {1890 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int321891 // global_tid);1892 uint32_t SrcLocStrSize;1893 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);1894 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);1895 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};1896 1897 // Ignore return result until untied tasks are supported.1898 createRuntimeFunctionCall(1899 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);1900}1901 1902void OpenMPIRBuilder::createTaskwait(const LocationDescription &Loc) {1903 if (!updateToLocation(Loc))1904 return;1905 emitTaskwaitImpl(Loc);1906}1907 1908void OpenMPIRBuilder::emitTaskyieldImpl(const LocationDescription &Loc) {1909 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);1910 uint32_t SrcLocStrSize;1911 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);1912 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);1913 Constant *I32Null = ConstantInt::getNullValue(Int32);1914 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};1915 1916 createRuntimeFunctionCall(1917 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);1918}1919 1920void OpenMPIRBuilder::createTaskyield(const LocationDescription &Loc) {1921 if (!updateToLocation(Loc))1922 return;1923 emitTaskyieldImpl(Loc);1924}1925 1926// Processes the dependencies in Dependencies and does the following1927// - Allocates space on the stack of an array of DependInfo objects1928// - Populates each DependInfo object with relevant information of1929// the corresponding dependence.1930// - All code is inserted in the entry block of the current function.1931static Value *emitTaskDependencies(1932 OpenMPIRBuilder &OMPBuilder,1933 const SmallVectorImpl<OpenMPIRBuilder::DependData> &Dependencies) {1934 // Early return if we have no dependencies to process1935 if (Dependencies.empty())1936 return nullptr;1937 1938 // Given a vector of DependData objects, in this function we create an1939 // array on the stack that holds kmp_dep_info objects corresponding1940 // to each dependency. This is then passed to the OpenMP runtime.1941 // For example, if there are 'n' dependencies then the following psedo1942 // code is generated. Assume the first dependence is on a variable 'a'1943 //1944 // \code{c}1945 // DepArray = alloc(n x sizeof(kmp_depend_info);1946 // idx = 0;1947 // DepArray[idx].base_addr = ptrtoint(&a);1948 // DepArray[idx].len = 8;1949 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/1950 // ++idx;1951 // DepArray[idx].base_addr = ...;1952 // \endcode1953 1954 IRBuilderBase &Builder = OMPBuilder.Builder;1955 Type *DependInfo = OMPBuilder.DependInfo;1956 Module &M = OMPBuilder.M;1957 1958 Value *DepArray = nullptr;1959 OpenMPIRBuilder::InsertPointTy OldIP = Builder.saveIP();1960 Builder.SetInsertPoint(1961 OldIP.getBlock()->getParent()->getEntryBlock().getTerminator());1962 1963 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());1964 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");1965 1966 Builder.restoreIP(OldIP);1967 1968 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {1969 Value *Base =1970 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);1971 // Store the pointer to the variable1972 Value *Addr = Builder.CreateStructGEP(1973 DependInfo, Base,1974 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));1975 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, Builder.getInt64Ty());1976 Builder.CreateStore(DepValPtr, Addr);1977 // Store the size of the variable1978 Value *Size = Builder.CreateStructGEP(1979 DependInfo, Base, static_cast<unsigned int>(RTLDependInfoFields::Len));1980 Builder.CreateStore(1981 Builder.getInt64(M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),1982 Size);1983 // Store the dependency kind1984 Value *Flags = Builder.CreateStructGEP(1985 DependInfo, Base,1986 static_cast<unsigned int>(RTLDependInfoFields::Flags));1987 Builder.CreateStore(1988 ConstantInt::get(Builder.getInt8Ty(),1989 static_cast<unsigned int>(Dep.DepKind)),1990 Flags);1991 }1992 return DepArray;1993}1994 1995OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTask(1996 const LocationDescription &Loc, InsertPointTy AllocaIP,1997 BodyGenCallbackTy BodyGenCB, bool Tied, Value *Final, Value *IfCondition,1998 SmallVector<DependData> Dependencies, bool Mergeable, Value *EventHandle,1999 Value *Priority) {2000 2001 if (!updateToLocation(Loc))2002 return InsertPointTy();2003 2004 uint32_t SrcLocStrSize;2005 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);2006 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);2007 // The current basic block is split into four basic blocks. After outlining,2008 // they will be mapped as follows:2009 // ```2010 // def current_fn() {2011 // current_basic_block:2012 // br label %task.exit2013 // task.exit:2014 // ; instructions after task2015 // }2016 // def outlined_fn() {2017 // task.alloca:2018 // br label %task.body2019 // task.body:2020 // ret void2021 // }2022 // ```2023 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");2024 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");2025 BasicBlock *TaskAllocaBB =2026 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");2027 2028 InsertPointTy TaskAllocaIP =2029 InsertPointTy(TaskAllocaBB, TaskAllocaBB->begin());2030 InsertPointTy TaskBodyIP = InsertPointTy(TaskBodyBB, TaskBodyBB->begin());2031 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP))2032 return Err;2033 2034 OutlineInfo OI;2035 OI.EntryBB = TaskAllocaBB;2036 OI.OuterAllocaBB = AllocaIP.getBlock();2037 OI.ExitBB = TaskExitBB;2038 2039 // Add the thread ID argument.2040 SmallVector<Instruction *, 4> ToBeDeleted;2041 OI.ExcludeArgsFromAggregate.push_back(createFakeIntVal(2042 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));2043 2044 OI.PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,2045 Mergeable, Priority, EventHandle, TaskAllocaBB,2046 ToBeDeleted](Function &OutlinedFn) mutable {2047 // Replace the Stale CI by appropriate RTL function call.2048 assert(OutlinedFn.hasOneUse() &&2049 "there must be a single user for the outlined function");2050 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());2051 2052 // HasShareds is true if any variables are captured in the outlined region,2053 // false otherwise.2054 bool HasShareds = StaleCI->arg_size() > 1;2055 Builder.SetInsertPoint(StaleCI);2056 2057 // Gather the arguments for emitting the runtime call for2058 // @__kmpc_omp_task_alloc2059 Function *TaskAllocFn =2060 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);2061 2062 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)2063 // call.2064 Value *ThreadID = getOrCreateThreadID(Ident);2065 2066 // Argument - `flags`2067 // Task is tied iff (Flags & 1) == 1.2068 // Task is untied iff (Flags & 1) == 0.2069 // Task is final iff (Flags & 2) == 2.2070 // Task is not final iff (Flags & 2) == 0.2071 // Task is mergeable iff (Flags & 4) == 4.2072 // Task is not mergeable iff (Flags & 4) == 0.2073 // Task is priority iff (Flags & 32) == 32.2074 // Task is not priority iff (Flags & 32) == 0.2075 // TODO: Handle the other flags.2076 Value *Flags = Builder.getInt32(Tied);2077 if (Final) {2078 Value *FinalFlag =2079 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));2080 Flags = Builder.CreateOr(FinalFlag, Flags);2081 }2082 2083 if (Mergeable)2084 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);2085 if (Priority)2086 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);2087 2088 // Argument - `sizeof_kmp_task_t` (TaskSize)2089 // Tasksize refers to the size in bytes of kmp_task_t data structure2090 // including private vars accessed in task.2091 // TODO: add kmp_task_t_with_privates (privates)2092 Value *TaskSize = Builder.getInt64(2093 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));2094 2095 // Argument - `sizeof_shareds` (SharedsSize)2096 // SharedsSize refers to the shareds array size in the kmp_task_t data2097 // structure.2098 Value *SharedsSize = Builder.getInt64(0);2099 if (HasShareds) {2100 AllocaInst *ArgStructAlloca =2101 dyn_cast<AllocaInst>(StaleCI->getArgOperand(1));2102 assert(ArgStructAlloca &&2103 "Unable to find the alloca instruction corresponding to arguments "2104 "for extracted function");2105 StructType *ArgStructType =2106 dyn_cast<StructType>(ArgStructAlloca->getAllocatedType());2107 assert(ArgStructType && "Unable to find struct type corresponding to "2108 "arguments for extracted function");2109 SharedsSize =2110 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ArgStructType));2111 }2112 // Emit the @__kmpc_omp_task_alloc runtime call2113 // The runtime call returns a pointer to an area where the task captured2114 // variables must be copied before the task is run (TaskData)2115 CallInst *TaskData = createRuntimeFunctionCall(2116 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,2117 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,2118 /*task_func=*/&OutlinedFn});2119 2120 // Emit detach clause initialization.2121 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,2122 // task_descriptor);2123 if (EventHandle) {2124 Function *TaskDetachFn = getOrCreateRuntimeFunctionPtr(2125 OMPRTL___kmpc_task_allow_completion_event);2126 llvm::Value *EventVal =2127 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});2128 llvm::Value *EventHandleAddr =2129 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,2130 Builder.getPtrTy(0));2131 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());2132 Builder.CreateStore(EventVal, EventHandleAddr);2133 }2134 // Copy the arguments for outlined function2135 if (HasShareds) {2136 Value *Shareds = StaleCI->getArgOperand(1);2137 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());2138 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);2139 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,2140 SharedsSize);2141 }2142 2143 if (Priority) {2144 //2145 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",2146 // we populate the priority information into the "kmp_task_t" here2147 //2148 // The struct "kmp_task_t" definition is available in kmp.h2149 // kmp_task_t = { shareds, routine, part_id, data1, data2 }2150 // data2 is used for priority2151 //2152 Type *Int32Ty = Builder.getInt32Ty();2153 Constant *Zero = ConstantInt::get(Int32Ty, 0);2154 // kmp_task_t* => { ptr }2155 Type *TaskPtr = StructType::get(VoidPtr);2156 Value *TaskGEP =2157 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});2158 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }2159 Type *TaskStructType = StructType::get(2160 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);2161 Value *PriorityData = Builder.CreateInBoundsGEP(2162 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});2163 // kmp_cmplrdata_t => { ptr, ptr }2164 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);2165 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,2166 PriorityData, {Zero, Zero});2167 Builder.CreateStore(Priority, CmplrData);2168 }2169 2170 Value *DepArray = emitTaskDependencies(*this, Dependencies);2171 2172 // In the presence of the `if` clause, the following IR is generated:2173 // ...2174 // %data = call @__kmpc_omp_task_alloc(...)2175 // br i1 %if_condition, label %then, label %else2176 // then:2177 // call @__kmpc_omp_task(...)2178 // br label %exit2179 // else:2180 // ;; Wait for resolution of dependencies, if any, before2181 // ;; beginning the task2182 // call @__kmpc_omp_wait_deps(...)2183 // call @__kmpc_omp_task_begin_if0(...)2184 // call @outlined_fn(...)2185 // call @__kmpc_omp_task_complete_if0(...)2186 // br label %exit2187 // exit:2188 // ...2189 if (IfCondition) {2190 // `SplitBlockAndInsertIfThenElse` requires the block to have a2191 // terminator.2192 splitBB(Builder, /*CreateBranch=*/true, "if.end");2193 Instruction *IfTerminator =2194 Builder.GetInsertPoint()->getParent()->getTerminator();2195 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;2196 Builder.SetInsertPoint(IfTerminator);2197 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,2198 &ElseTI);2199 Builder.SetInsertPoint(ElseTI);2200 2201 if (Dependencies.size()) {2202 Function *TaskWaitFn =2203 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);2204 createRuntimeFunctionCall(2205 TaskWaitFn,2206 {Ident, ThreadID, Builder.getInt32(Dependencies.size()), DepArray,2207 ConstantInt::get(Builder.getInt32Ty(), 0),2208 ConstantPointerNull::get(PointerType::getUnqual(M.getContext()))});2209 }2210 Function *TaskBeginFn =2211 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);2212 Function *TaskCompleteFn =2213 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);2214 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});2215 CallInst *CI = nullptr;2216 if (HasShareds)2217 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});2218 else2219 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});2220 CI->setDebugLoc(StaleCI->getDebugLoc());2221 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});2222 Builder.SetInsertPoint(ThenTI);2223 }2224 2225 if (Dependencies.size()) {2226 Function *TaskFn =2227 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);2228 createRuntimeFunctionCall(2229 TaskFn,2230 {Ident, ThreadID, TaskData, Builder.getInt32(Dependencies.size()),2231 DepArray, ConstantInt::get(Builder.getInt32Ty(), 0),2232 ConstantPointerNull::get(PointerType::getUnqual(M.getContext()))});2233 2234 } else {2235 // Emit the @__kmpc_omp_task runtime call to spawn the task2236 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);2237 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});2238 }2239 2240 StaleCI->eraseFromParent();2241 2242 Builder.SetInsertPoint(TaskAllocaBB, TaskAllocaBB->begin());2243 if (HasShareds) {2244 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));2245 OutlinedFn.getArg(1)->replaceUsesWithIf(2246 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });2247 }2248 2249 for (Instruction *I : llvm::reverse(ToBeDeleted))2250 I->eraseFromParent();2251 };2252 2253 addOutlineInfo(std::move(OI));2254 Builder.SetInsertPoint(TaskExitBB, TaskExitBB->begin());2255 2256 return Builder.saveIP();2257}2258 2259OpenMPIRBuilder::InsertPointOrErrorTy2260OpenMPIRBuilder::createTaskgroup(const LocationDescription &Loc,2261 InsertPointTy AllocaIP,2262 BodyGenCallbackTy BodyGenCB) {2263 if (!updateToLocation(Loc))2264 return InsertPointTy();2265 2266 uint32_t SrcLocStrSize;2267 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);2268 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);2269 Value *ThreadID = getOrCreateThreadID(Ident);2270 2271 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup2272 Function *TaskgroupFn =2273 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);2274 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});2275 2276 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");2277 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP()))2278 return Err;2279 2280 Builder.SetInsertPoint(TaskgroupExitBB);2281 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup2282 Function *EndTaskgroupFn =2283 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);2284 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});2285 2286 return Builder.saveIP();2287}2288 2289OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createSections(2290 const LocationDescription &Loc, InsertPointTy AllocaIP,2291 ArrayRef<StorableBodyGenCallbackTy> SectionCBs, PrivatizeCallbackTy PrivCB,2292 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {2293 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");2294 2295 if (!updateToLocation(Loc))2296 return Loc.IP;2297 2298 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});2299 2300 // Each section is emitted as a switch case2301 // Each finalization callback is handled from clang.EmitOMPSectionDirective()2302 // -> OMP.createSection() which generates the IR for each section2303 // Iterate through all sections and emit a switch construct:2304 // switch (IV) {2305 // case 0:2306 // <SectionStmt[0]>;2307 // break;2308 // ...2309 // case <NumSection> - 1:2310 // <SectionStmt[<NumSection> - 1]>;2311 // break;2312 // }2313 // ...2314 // section_loop.after:2315 // <FiniCB>;2316 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {2317 Builder.restoreIP(CodeGenIP);2318 BasicBlock *Continue =2319 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");2320 Function *CurFn = Continue->getParent();2321 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);2322 2323 unsigned CaseNumber = 0;2324 for (auto SectionCB : SectionCBs) {2325 BasicBlock *CaseBB = BasicBlock::Create(2326 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);2327 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);2328 Builder.SetInsertPoint(CaseBB);2329 BranchInst *CaseEndBr = Builder.CreateBr(Continue);2330 if (Error Err = SectionCB(InsertPointTy(), {CaseEndBr->getParent(),2331 CaseEndBr->getIterator()}))2332 return Err;2333 CaseNumber++;2334 }2335 // remove the existing terminator from body BB since there can be no2336 // terminators after switch/case2337 return Error::success();2338 };2339 // Loop body ends here2340 // LowerBound, UpperBound, and STride for createCanonicalLoop2341 Type *I32Ty = Type::getInt32Ty(M.getContext());2342 Value *LB = ConstantInt::get(I32Ty, 0);2343 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());2344 Value *ST = ConstantInt::get(I32Ty, 1);2345 Expected<CanonicalLoopInfo *> LoopInfo = createCanonicalLoop(2346 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");2347 if (!LoopInfo)2348 return LoopInfo.takeError();2349 2350 InsertPointOrErrorTy WsloopIP =2351 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,2352 WorksharingLoopType::ForStaticLoop, !IsNowait);2353 if (!WsloopIP)2354 return WsloopIP.takeError();2355 InsertPointTy AfterIP = *WsloopIP;2356 2357 BasicBlock *LoopFini = AfterIP.getBlock()->getSinglePredecessor();2358 assert(LoopFini && "Bad structure of static workshare loop finalization");2359 2360 // Apply the finalization callback in LoopAfterBB2361 auto FiniInfo = FinalizationStack.pop_back_val();2362 assert(FiniInfo.DK == OMPD_sections &&2363 "Unexpected finalization stack state!");2364 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))2365 return Err;2366 2367 return AfterIP;2368}2369 2370OpenMPIRBuilder::InsertPointOrErrorTy2371OpenMPIRBuilder::createSection(const LocationDescription &Loc,2372 BodyGenCallbackTy BodyGenCB,2373 FinalizeCallbackTy FiniCB) {2374 if (!updateToLocation(Loc))2375 return Loc.IP;2376 2377 auto FiniCBWrapper = [&](InsertPointTy IP) {2378 if (IP.getBlock()->end() != IP.getPoint())2379 return FiniCB(IP);2380 // This must be done otherwise any nested constructs using FinalizeOMPRegion2381 // will fail because that function requires the Finalization Basic Block to2382 // have a terminator, which is already removed by EmitOMPRegionBody.2383 // IP is currently at cancelation block.2384 // We need to backtrack to the condition block to fetch2385 // the exit block and create a branch from cancelation2386 // to exit block.2387 IRBuilder<>::InsertPointGuard IPG(Builder);2388 Builder.restoreIP(IP);2389 auto *CaseBB = Loc.IP.getBlock();2390 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();2391 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);2392 Instruction *I = Builder.CreateBr(ExitBB);2393 IP = InsertPointTy(I->getParent(), I->getIterator());2394 return FiniCB(IP);2395 };2396 2397 Directive OMPD = Directive::OMPD_sections;2398 // Since we are using Finalization Callback here, HasFinalize2399 // and IsCancellable have to be true2400 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,2401 /*Conditional*/ false, /*hasFinalize*/ true,2402 /*IsCancellable*/ true);2403}2404 2405static OpenMPIRBuilder::InsertPointTy getInsertPointAfterInstr(Instruction *I) {2406 BasicBlock::iterator IT(I);2407 IT++;2408 return OpenMPIRBuilder::InsertPointTy(I->getParent(), IT);2409}2410 2411Value *OpenMPIRBuilder::getGPUThreadID() {2412 return createRuntimeFunctionCall(2413 getOrCreateRuntimeFunction(M,2414 OMPRTL___kmpc_get_hardware_thread_id_in_block),2415 {});2416}2417 2418Value *OpenMPIRBuilder::getGPUWarpSize() {2419 return createRuntimeFunctionCall(2420 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});2421}2422 2423Value *OpenMPIRBuilder::getNVPTXWarpID() {2424 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);2425 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");2426}2427 2428Value *OpenMPIRBuilder::getNVPTXLaneID() {2429 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);2430 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");2431 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);2432 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),2433 "nvptx_lane_id");2434}2435 2436Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,2437 Type *ToType) {2438 Type *FromType = From->getType();2439 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);2440 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);2441 assert(FromSize > 0 && "From size must be greater than zero");2442 assert(ToSize > 0 && "To size must be greater than zero");2443 if (FromType == ToType)2444 return From;2445 if (FromSize == ToSize)2446 return Builder.CreateBitCast(From, ToType);2447 if (ToType->isIntegerTy() && FromType->isIntegerTy())2448 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);2449 InsertPointTy SaveIP = Builder.saveIP();2450 Builder.restoreIP(AllocaIP);2451 Value *CastItem = Builder.CreateAlloca(ToType);2452 Builder.restoreIP(SaveIP);2453 2454 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(2455 CastItem, Builder.getPtrTy(0));2456 Builder.CreateStore(From, ValCastItem);2457 return Builder.CreateLoad(ToType, CastItem);2458}2459 2460Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,2461 Value *Element,2462 Type *ElementType,2463 Value *Offset) {2464 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);2465 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");2466 2467 // Cast all types to 32- or 64-bit values before calling shuffle routines.2468 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);2469 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);2470 Value *WarpSize =2471 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);2472 Function *ShuffleFunc = getOrCreateRuntimeFunctionPtr(2473 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int322474 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);2475 Value *WarpSizeCast =2476 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);2477 Value *ShuffleCall =2478 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});2479 return castValueToType(AllocaIP, ShuffleCall, CastTy);2480}2481 2482void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,2483 Value *DstAddr, Type *ElemType,2484 Value *Offset, Type *ReductionArrayTy,2485 bool IsByRefElem) {2486 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);2487 // Create the loop over the big sized data.2488 // ptr = (void*)Elem;2489 // ptrEnd = (void*) Elem + 1;2490 // Step = 8;2491 // while (ptr + Step < ptrEnd)2492 // shuffle((int64_t)*ptr);2493 // Step = 4;2494 // while (ptr + Step < ptrEnd)2495 // shuffle((int32_t)*ptr);2496 // ...2497 Type *IndexTy = Builder.getIndexTy(2498 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());2499 Value *ElemPtr = DstAddr;2500 Value *Ptr = SrcAddr;2501 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {2502 if (Size < IntSize)2503 continue;2504 Type *IntType = Builder.getIntNTy(IntSize * 8);2505 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(2506 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");2507 Value *SrcAddrGEP =2508 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});2509 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(2510 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");2511 2512 Function *CurFunc = Builder.GetInsertBlock()->getParent();2513 if ((Size / IntSize) > 1) {2514 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(2515 SrcAddrGEP, Builder.getPtrTy());2516 BasicBlock *PreCondBB =2517 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");2518 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");2519 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");2520 BasicBlock *CurrentBB = Builder.GetInsertBlock();2521 emitBlock(PreCondBB, CurFunc);2522 PHINode *PhiSrc =2523 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);2524 PhiSrc->addIncoming(Ptr, CurrentBB);2525 PHINode *PhiDest =2526 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);2527 PhiDest->addIncoming(ElemPtr, CurrentBB);2528 Ptr = PhiSrc;2529 ElemPtr = PhiDest;2530 Value *PtrDiff = Builder.CreatePtrDiff(2531 Builder.getInt8Ty(), PtrEnd,2532 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));2533 Builder.CreateCondBr(2534 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,2535 ExitBB);2536 emitBlock(ThenBB, CurFunc);2537 Value *Res = createRuntimeShuffleFunction(2538 AllocaIP,2539 Builder.CreateAlignedLoad(2540 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),2541 IntType, Offset);2542 Builder.CreateAlignedStore(Res, ElemPtr,2543 M.getDataLayout().getPrefTypeAlign(ElemType));2544 Value *LocalPtr =2545 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});2546 Value *LocalElemPtr =2547 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});2548 PhiSrc->addIncoming(LocalPtr, ThenBB);2549 PhiDest->addIncoming(LocalElemPtr, ThenBB);2550 emitBranch(PreCondBB);2551 emitBlock(ExitBB, CurFunc);2552 } else {2553 Value *Res = createRuntimeShuffleFunction(2554 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);2555 if (ElemType->isIntegerTy() && ElemType->getScalarSizeInBits() <2556 Res->getType()->getScalarSizeInBits())2557 Res = Builder.CreateTrunc(Res, ElemType);2558 Builder.CreateStore(Res, ElemPtr);2559 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});2560 ElemPtr =2561 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});2562 }2563 Size = Size % IntSize;2564 }2565}2566 2567Error OpenMPIRBuilder::emitReductionListCopy(2568 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,2569 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,2570 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {2571 Type *IndexTy = Builder.getIndexTy(2572 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());2573 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;2574 2575 // Iterates, element-by-element, through the source Reduce list and2576 // make a copy.2577 for (auto En : enumerate(ReductionInfos)) {2578 const ReductionInfo &RI = En.value();2579 Value *SrcElementAddr = nullptr;2580 AllocaInst *DestAlloca = nullptr;2581 Value *DestElementAddr = nullptr;2582 Value *DestElementPtrAddr = nullptr;2583 // Should we shuffle in an element from a remote lane?2584 bool ShuffleInElement = false;2585 // Set to true to update the pointer in the dest Reduce list to a2586 // newly created element.2587 bool UpdateDestListPtr = false;2588 2589 // Step 1.1: Get the address for the src element in the Reduce list.2590 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(2591 ReductionArrayTy, SrcBase,2592 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});2593 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);2594 2595 // Step 1.2: Create a temporary to store the element in the destination2596 // Reduce list.2597 DestElementPtrAddr = Builder.CreateInBoundsGEP(2598 ReductionArrayTy, DestBase,2599 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});2600 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);2601 switch (Action) {2602 case CopyAction::RemoteLaneToThread: {2603 InsertPointTy CurIP = Builder.saveIP();2604 Builder.restoreIP(AllocaIP);2605 2606 Type *DestAllocaType =2607 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;2608 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,2609 ".omp.reduction.element");2610 DestAlloca->setAlignment(2611 M.getDataLayout().getPrefTypeAlign(DestAllocaType));2612 DestElementAddr = DestAlloca;2613 DestElementAddr =2614 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),2615 DestElementAddr->getName() + ".ascast");2616 Builder.restoreIP(CurIP);2617 ShuffleInElement = true;2618 UpdateDestListPtr = true;2619 break;2620 }2621 case CopyAction::ThreadCopy: {2622 DestElementAddr =2623 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);2624 break;2625 }2626 }2627 2628 // Now that all active lanes have read the element in the2629 // Reduce list, shuffle over the value from the remote lane.2630 if (ShuffleInElement) {2631 Type *ShuffleType = RI.ElementType;2632 Value *ShuffleSrcAddr = SrcElementAddr;2633 Value *ShuffleDestAddr = DestElementAddr;2634 AllocaInst *LocalStorage = nullptr;2635 2636 if (IsByRefElem) {2637 assert(RI.ByRefElementType && "Expected by-ref element type to be set");2638 assert(RI.ByRefAllocatedType &&2639 "Expected by-ref allocated type to be set");2640 // For by-ref reductions, we need to copy from the remote lane the2641 // actual value of the partial reduction computed by that remote lane;2642 // rather than, for example, a pointer to that data or, even worse, a2643 // pointer to the descriptor of the by-ref reduction element.2644 ShuffleType = RI.ByRefElementType;2645 2646 InsertPointOrErrorTy GenResult =2647 RI.DataPtrPtrGen(Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);2648 2649 if (!GenResult)2650 return GenResult.takeError();2651 2652 ShuffleSrcAddr = Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);2653 2654 {2655 InsertPointTy OldIP = Builder.saveIP();2656 Builder.restoreIP(AllocaIP);2657 2658 LocalStorage = Builder.CreateAlloca(ShuffleType);2659 Builder.restoreIP(OldIP);2660 ShuffleDestAddr = LocalStorage;2661 }2662 }2663 2664 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,2665 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);2666 2667 if (IsByRefElem) {2668 Value *GEP;2669 InsertPointOrErrorTy GenResult =2670 RI.DataPtrPtrGen(Builder.saveIP(),2671 Builder.CreatePointerBitCastOrAddrSpaceCast(2672 DestAlloca, Builder.getPtrTy(), ".ascast"),2673 GEP);2674 2675 if (!GenResult)2676 return GenResult.takeError();2677 2678 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(2679 LocalStorage, Builder.getPtrTy(), ".ascast"),2680 GEP);2681 }2682 } else {2683 switch (RI.EvaluationKind) {2684 case EvalKind::Scalar: {2685 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);2686 // Store the source element value to the dest element address.2687 Builder.CreateStore(Elem, DestElementAddr);2688 break;2689 }2690 case EvalKind::Complex: {2691 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(2692 RI.ElementType, SrcElementAddr, 0, 0, ".realp");2693 Value *SrcReal = Builder.CreateLoad(2694 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");2695 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(2696 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");2697 Value *SrcImg = Builder.CreateLoad(2698 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");2699 2700 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(2701 RI.ElementType, DestElementAddr, 0, 0, ".realp");2702 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(2703 RI.ElementType, DestElementAddr, 0, 1, ".imagp");2704 Builder.CreateStore(SrcReal, DestRealPtr);2705 Builder.CreateStore(SrcImg, DestImgPtr);2706 break;2707 }2708 case EvalKind::Aggregate: {2709 Value *SizeVal = Builder.getInt64(2710 M.getDataLayout().getTypeStoreSize(RI.ElementType));2711 Builder.CreateMemCpy(2712 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),2713 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),2714 SizeVal, false);2715 break;2716 }2717 };2718 }2719 2720 // Step 3.1: Modify reference in dest Reduce list as needed.2721 // Modifying the reference in Reduce list to point to the newly2722 // created element. The element is live in the current function2723 // scope and that of functions it invokes (i.e., reduce_function).2724 // RemoteReduceData[i] = (void*)&RemoteElem2725 if (UpdateDestListPtr) {2726 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(2727 DestElementAddr, Builder.getPtrTy(),2728 DestElementAddr->getName() + ".ascast");2729 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);2730 }2731 }2732 2733 return Error::success();2734}2735 2736Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(2737 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,2738 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {2739 InsertPointTy SavedIP = Builder.saveIP();2740 LLVMContext &Ctx = M.getContext();2741 FunctionType *FuncTy = FunctionType::get(2742 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},2743 /* IsVarArg */ false);2744 Function *WcFunc =2745 Function::Create(FuncTy, GlobalVariable::InternalLinkage,2746 "_omp_reduction_inter_warp_copy_func", &M);2747 WcFunc->setAttributes(FuncAttrs);2748 WcFunc->addParamAttr(0, Attribute::NoUndef);2749 WcFunc->addParamAttr(1, Attribute::NoUndef);2750 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);2751 Builder.SetInsertPoint(EntryBB);2752 2753 // ReduceList: thread local Reduce list.2754 // At the stage of the computation when this function is called, partially2755 // aggregated values reside in the first lane of every active warp.2756 Argument *ReduceListArg = WcFunc->getArg(0);2757 // NumWarps: number of warps active in the parallel region. This could2758 // be smaller than 32 (max warps in a CTA) for partial block reduction.2759 Argument *NumWarpsArg = WcFunc->getArg(1);2760 2761 // This array is used as a medium to transfer, one reduce element at a time,2762 // the data from the first lane of every warp to lanes in the first warp2763 // in order to perform the final step of a reduction in a parallel region2764 // (reduction across warps). The array is placed in NVPTX __shared__ memory2765 // for reduced latency, as well as to have a distinct copy for concurrently2766 // executing target regions. The array is declared with common linkage so2767 // as to be shared across compilation units.2768 StringRef TransferMediumName =2769 "__openmp_nvptx_data_transfer_temporary_storage";2770 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);2771 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;2772 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);2773 if (!TransferMedium) {2774 TransferMedium = new GlobalVariable(2775 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,2776 UndefValue::get(ArrayTy), TransferMediumName,2777 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,2778 /*AddressSpace=*/3);2779 }2780 2781 // Get the CUDA thread id of the current OpenMP thread on the GPU.2782 Value *GPUThreadID = getGPUThreadID();2783 // nvptx_lane_id = nvptx_id % warpsize2784 Value *LaneID = getNVPTXLaneID();2785 // nvptx_warp_id = nvptx_id / warpsize2786 Value *WarpID = getNVPTXWarpID();2787 2788 InsertPointTy AllocaIP =2789 InsertPointTy(Builder.GetInsertBlock(),2790 Builder.GetInsertBlock()->getFirstInsertionPt());2791 Type *Arg0Type = ReduceListArg->getType();2792 Type *Arg1Type = NumWarpsArg->getType();2793 Builder.restoreIP(AllocaIP);2794 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(2795 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");2796 AllocaInst *NumWarpsAlloca =2797 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");2798 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(2799 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");2800 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(2801 NumWarpsAlloca, Builder.getPtrTy(0),2802 NumWarpsAlloca->getName() + ".ascast");2803 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);2804 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);2805 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);2806 InsertPointTy CodeGenIP =2807 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());2808 Builder.restoreIP(CodeGenIP);2809 2810 Value *ReduceList =2811 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);2812 2813 for (auto En : enumerate(ReductionInfos)) {2814 //2815 // Warp master copies reduce element to transfer medium in __shared__2816 // memory.2817 //2818 const ReductionInfo &RI = En.value();2819 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];2820 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(2821 IsByRefElem ? RI.ByRefElementType : RI.ElementType);2822 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {2823 Type *CType = Builder.getIntNTy(TySize * 8);2824 2825 unsigned NumIters = RealTySize / TySize;2826 if (NumIters == 0)2827 continue;2828 Value *Cnt = nullptr;2829 Value *CntAddr = nullptr;2830 BasicBlock *PrecondBB = nullptr;2831 BasicBlock *ExitBB = nullptr;2832 if (NumIters > 1) {2833 CodeGenIP = Builder.saveIP();2834 Builder.restoreIP(AllocaIP);2835 CntAddr =2836 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");2837 2838 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),2839 CntAddr->getName() + ".ascast");2840 Builder.restoreIP(CodeGenIP);2841 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),2842 CntAddr,2843 /*Volatile=*/false);2844 PrecondBB = BasicBlock::Create(Ctx, "precond");2845 ExitBB = BasicBlock::Create(Ctx, "exit");2846 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");2847 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());2848 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,2849 /*Volatile=*/false);2850 Value *Cmp = Builder.CreateICmpULT(2851 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));2852 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);2853 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());2854 }2855 2856 // kmpc_barrier.2857 InsertPointOrErrorTy BarrierIP1 =2858 createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),2859 omp::Directive::OMPD_unknown,2860 /* ForceSimpleCall */ false,2861 /* CheckCancelFlag */ true);2862 if (!BarrierIP1)2863 return BarrierIP1.takeError();2864 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");2865 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");2866 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");2867 2868 // if (lane_id == 0)2869 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");2870 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);2871 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());2872 2873 // Reduce element = LocalReduceList[i]2874 auto *RedListArrayTy =2875 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());2876 Type *IndexTy = Builder.getIndexTy(2877 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());2878 Value *ElemPtrPtr =2879 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,2880 {ConstantInt::get(IndexTy, 0),2881 ConstantInt::get(IndexTy, En.index())});2882 // elemptr = ((CopyType*)(elemptrptr)) + I2883 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);2884 2885 if (IsByRefElem) {2886 InsertPointOrErrorTy GenRes =2887 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);2888 2889 if (!GenRes)2890 return GenRes.takeError();2891 2892 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);2893 }2894 2895 if (NumIters > 1)2896 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);2897 2898 // Get pointer to location in transfer medium.2899 // MediumPtr = &medium[warp_id]2900 Value *MediumPtr = Builder.CreateInBoundsGEP(2901 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});2902 // elem = *elemptr2903 //*MediumPtr = elem2904 Value *Elem = Builder.CreateLoad(CType, ElemPtr);2905 // Store the source element value to the dest element address.2906 Builder.CreateStore(Elem, MediumPtr,2907 /*IsVolatile*/ true);2908 Builder.CreateBr(MergeBB);2909 2910 // else2911 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());2912 Builder.CreateBr(MergeBB);2913 2914 // endif2915 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());2916 InsertPointOrErrorTy BarrierIP2 =2917 createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),2918 omp::Directive::OMPD_unknown,2919 /* ForceSimpleCall */ false,2920 /* CheckCancelFlag */ true);2921 if (!BarrierIP2)2922 return BarrierIP2.takeError();2923 2924 // Warp 0 copies reduce element from transfer medium2925 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");2926 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");2927 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");2928 2929 Value *NumWarpsVal =2930 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);2931 // Up to 32 threads in warp 0 are active.2932 Value *IsActiveThread =2933 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");2934 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);2935 2936 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());2937 2938 // SecMediumPtr = &medium[tid]2939 // SrcMediumVal = *SrcMediumPtr2940 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(2941 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});2942 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I2943 Value *TargetElemPtrPtr =2944 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,2945 {ConstantInt::get(IndexTy, 0),2946 ConstantInt::get(IndexTy, En.index())});2947 Value *TargetElemPtrVal =2948 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);2949 Value *TargetElemPtr = TargetElemPtrVal;2950 2951 if (IsByRefElem) {2952 InsertPointOrErrorTy GenRes =2953 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);2954 2955 if (!GenRes)2956 return GenRes.takeError();2957 2958 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);2959 }2960 2961 if (NumIters > 1)2962 TargetElemPtr =2963 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);2964 2965 // *TargetElemPtr = SrcMediumVal;2966 Value *SrcMediumValue =2967 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);2968 Builder.CreateStore(SrcMediumValue, TargetElemPtr);2969 Builder.CreateBr(W0MergeBB);2970 2971 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());2972 Builder.CreateBr(W0MergeBB);2973 2974 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());2975 2976 if (NumIters > 1) {2977 Cnt = Builder.CreateNSWAdd(2978 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));2979 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);2980 2981 auto *CurFn = Builder.GetInsertBlock()->getParent();2982 emitBranch(PrecondBB);2983 emitBlock(ExitBB, CurFn);2984 }2985 RealTySize %= TySize;2986 }2987 }2988 2989 Builder.CreateRetVoid();2990 Builder.restoreIP(SavedIP);2991 2992 return WcFunc;2993}2994 2995Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(2996 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,2997 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {2998 LLVMContext &Ctx = M.getContext();2999 FunctionType *FuncTy =3000 FunctionType::get(Builder.getVoidTy(),3001 {Builder.getPtrTy(), Builder.getInt16Ty(),3002 Builder.getInt16Ty(), Builder.getInt16Ty()},3003 /* IsVarArg */ false);3004 Function *SarFunc =3005 Function::Create(FuncTy, GlobalVariable::InternalLinkage,3006 "_omp_reduction_shuffle_and_reduce_func", &M);3007 SarFunc->setAttributes(FuncAttrs);3008 SarFunc->addParamAttr(0, Attribute::NoUndef);3009 SarFunc->addParamAttr(1, Attribute::NoUndef);3010 SarFunc->addParamAttr(2, Attribute::NoUndef);3011 SarFunc->addParamAttr(3, Attribute::NoUndef);3012 SarFunc->addParamAttr(1, Attribute::SExt);3013 SarFunc->addParamAttr(2, Attribute::SExt);3014 SarFunc->addParamAttr(3, Attribute::SExt);3015 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);3016 Builder.SetInsertPoint(EntryBB);3017 3018 // Thread local Reduce list used to host the values of data to be reduced.3019 Argument *ReduceListArg = SarFunc->getArg(0);3020 // Current lane id; could be logical.3021 Argument *LaneIDArg = SarFunc->getArg(1);3022 // Offset of the remote source lane relative to the current lane.3023 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);3024 // Algorithm version. This is expected to be known at compile time.3025 Argument *AlgoVerArg = SarFunc->getArg(3);3026 3027 Type *ReduceListArgType = ReduceListArg->getType();3028 Type *LaneIDArgType = LaneIDArg->getType();3029 Type *LaneIDArgPtrType = Builder.getPtrTy(0);3030 Value *ReduceListAlloca = Builder.CreateAlloca(3031 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");3032 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,3033 LaneIDArg->getName() + ".addr");3034 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(3035 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");3036 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,3037 AlgoVerArg->getName() + ".addr");3038 ArrayType *RedListArrayTy =3039 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());3040 3041 // Create a local thread-private variable to host the Reduce list3042 // from a remote lane.3043 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(3044 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");3045 3046 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3047 ReduceListAlloca, ReduceListArgType,3048 ReduceListAlloca->getName() + ".ascast");3049 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3050 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");3051 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3052 RemoteLaneOffsetAlloca, LaneIDArgPtrType,3053 RemoteLaneOffsetAlloca->getName() + ".ascast");3054 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3055 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");3056 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3057 RemoteReductionListAlloca, Builder.getPtrTy(),3058 RemoteReductionListAlloca->getName() + ".ascast");3059 3060 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);3061 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);3062 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);3063 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);3064 3065 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);3066 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);3067 Value *RemoteLaneOffset =3068 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);3069 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);3070 3071 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);3072 3073 // This loop iterates through the list of reduce elements and copies,3074 // element by element, from a remote lane in the warp to RemoteReduceList,3075 // hosted on the thread's stack.3076 Error EmitRedLsCpRes = emitReductionListCopy(3077 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,3078 ReduceList, RemoteListAddrCast, IsByRef,3079 {RemoteLaneOffset, nullptr, nullptr});3080 3081 if (EmitRedLsCpRes)3082 return EmitRedLsCpRes;3083 3084 // The actions to be performed on the Remote Reduce list is dependent3085 // on the algorithm version.3086 //3087 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&3088 // LaneId % 2 == 0 && Offset > 0):3089 // do the reduction value aggregation3090 //3091 // The thread local variable Reduce list is mutated in place to host the3092 // reduced data, which is the aggregated value produced from local and3093 // remote lanes.3094 //3095 // Note that AlgoVer is expected to be a constant integer known at compile3096 // time.3097 // When AlgoVer==0, the first conjunction evaluates to true, making3098 // the entire predicate true during compile time.3099 // When AlgoVer==1, the second conjunction has only the second part to be3100 // evaluated during runtime. Other conjunctions evaluates to false3101 // during compile time.3102 // When AlgoVer==2, the third conjunction has only the second part to be3103 // evaluated during runtime. Other conjunctions evaluates to false3104 // during compile time.3105 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);3106 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));3107 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);3108 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);3109 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));3110 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));3111 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);3112 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);3113 Value *RemoteOffsetComp =3114 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));3115 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);3116 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);3117 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);3118 3119 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");3120 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");3121 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");3122 3123 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);3124 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());3125 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(3126 ReduceList, Builder.getPtrTy());3127 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(3128 RemoteListAddrCast, Builder.getPtrTy());3129 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})3130 ->addFnAttr(Attribute::NoUnwind);3131 Builder.CreateBr(MergeBB);3132 3133 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());3134 Builder.CreateBr(MergeBB);3135 3136 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());3137 3138 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local3139 // Reduce list.3140 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));3141 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);3142 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);3143 3144 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");3145 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");3146 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");3147 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);3148 3149 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());3150 3151 EmitRedLsCpRes = emitReductionListCopy(3152 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,3153 RemoteListAddrCast, ReduceList, IsByRef);3154 3155 if (EmitRedLsCpRes)3156 return EmitRedLsCpRes;3157 3158 Builder.CreateBr(CpyMergeBB);3159 3160 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());3161 Builder.CreateBr(CpyMergeBB);3162 3163 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());3164 3165 Builder.CreateRetVoid();3166 3167 return SarFunc;3168}3169 3170Function *OpenMPIRBuilder::emitListToGlobalCopyFunction(3171 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,3172 AttributeList FuncAttrs) {3173 OpenMPIRBuilder::InsertPointTy OldIP = Builder.saveIP();3174 LLVMContext &Ctx = M.getContext();3175 FunctionType *FuncTy = FunctionType::get(3176 Builder.getVoidTy(),3177 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},3178 /* IsVarArg */ false);3179 Function *LtGCFunc =3180 Function::Create(FuncTy, GlobalVariable::InternalLinkage,3181 "_omp_reduction_list_to_global_copy_func", &M);3182 LtGCFunc->setAttributes(FuncAttrs);3183 LtGCFunc->addParamAttr(0, Attribute::NoUndef);3184 LtGCFunc->addParamAttr(1, Attribute::NoUndef);3185 LtGCFunc->addParamAttr(2, Attribute::NoUndef);3186 3187 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);3188 Builder.SetInsertPoint(EntryBlock);3189 3190 // Buffer: global reduction buffer.3191 Argument *BufferArg = LtGCFunc->getArg(0);3192 // Idx: index of the buffer.3193 Argument *IdxArg = LtGCFunc->getArg(1);3194 // ReduceList: thread local Reduce list.3195 Argument *ReduceListArg = LtGCFunc->getArg(2);3196 3197 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,3198 BufferArg->getName() + ".addr");3199 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,3200 IdxArg->getName() + ".addr");3201 Value *ReduceListArgAlloca = Builder.CreateAlloca(3202 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");3203 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3204 BufferArgAlloca, Builder.getPtrTy(),3205 BufferArgAlloca->getName() + ".ascast");3206 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3207 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");3208 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3209 ReduceListArgAlloca, Builder.getPtrTy(),3210 ReduceListArgAlloca->getName() + ".ascast");3211 3212 Builder.CreateStore(BufferArg, BufferArgAddrCast);3213 Builder.CreateStore(IdxArg, IdxArgAddrCast);3214 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);3215 3216 Value *LocalReduceList =3217 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);3218 Value *BufferArgVal =3219 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);3220 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};3221 Type *IndexTy = Builder.getIndexTy(3222 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());3223 for (auto En : enumerate(ReductionInfos)) {3224 const ReductionInfo &RI = En.value();3225 auto *RedListArrayTy =3226 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());3227 // Reduce element = LocalReduceList[i]3228 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(3229 RedListArrayTy, LocalReduceList,3230 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});3231 // elemptr = ((CopyType*)(elemptrptr)) + I3232 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);3233 3234 // Global = Buffer.VD[Idx];3235 Value *BufferVD =3236 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);3237 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(3238 ReductionsBufferTy, BufferVD, 0, En.index());3239 3240 switch (RI.EvaluationKind) {3241 case EvalKind::Scalar: {3242 Value *TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);3243 Builder.CreateStore(TargetElement, GlobVal);3244 break;3245 }3246 case EvalKind::Complex: {3247 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(3248 RI.ElementType, ElemPtr, 0, 0, ".realp");3249 Value *SrcReal = Builder.CreateLoad(3250 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");3251 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(3252 RI.ElementType, ElemPtr, 0, 1, ".imagp");3253 Value *SrcImg = Builder.CreateLoad(3254 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");3255 3256 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(3257 RI.ElementType, GlobVal, 0, 0, ".realp");3258 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(3259 RI.ElementType, GlobVal, 0, 1, ".imagp");3260 Builder.CreateStore(SrcReal, DestRealPtr);3261 Builder.CreateStore(SrcImg, DestImgPtr);3262 break;3263 }3264 case EvalKind::Aggregate: {3265 Value *SizeVal =3266 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));3267 Builder.CreateMemCpy(3268 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,3269 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);3270 break;3271 }3272 }3273 }3274 3275 Builder.CreateRetVoid();3276 Builder.restoreIP(OldIP);3277 return LtGCFunc;3278}3279 3280Function *OpenMPIRBuilder::emitListToGlobalReduceFunction(3281 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,3282 Type *ReductionsBufferTy, AttributeList FuncAttrs) {3283 OpenMPIRBuilder::InsertPointTy OldIP = Builder.saveIP();3284 LLVMContext &Ctx = M.getContext();3285 FunctionType *FuncTy = FunctionType::get(3286 Builder.getVoidTy(),3287 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},3288 /* IsVarArg */ false);3289 Function *LtGRFunc =3290 Function::Create(FuncTy, GlobalVariable::InternalLinkage,3291 "_omp_reduction_list_to_global_reduce_func", &M);3292 LtGRFunc->setAttributes(FuncAttrs);3293 LtGRFunc->addParamAttr(0, Attribute::NoUndef);3294 LtGRFunc->addParamAttr(1, Attribute::NoUndef);3295 LtGRFunc->addParamAttr(2, Attribute::NoUndef);3296 3297 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);3298 Builder.SetInsertPoint(EntryBlock);3299 3300 // Buffer: global reduction buffer.3301 Argument *BufferArg = LtGRFunc->getArg(0);3302 // Idx: index of the buffer.3303 Argument *IdxArg = LtGRFunc->getArg(1);3304 // ReduceList: thread local Reduce list.3305 Argument *ReduceListArg = LtGRFunc->getArg(2);3306 3307 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,3308 BufferArg->getName() + ".addr");3309 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,3310 IdxArg->getName() + ".addr");3311 Value *ReduceListArgAlloca = Builder.CreateAlloca(3312 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");3313 auto *RedListArrayTy =3314 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());3315 3316 // 1. Build a list of reduction variables.3317 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};3318 Value *LocalReduceList =3319 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");3320 3321 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3322 BufferArgAlloca, Builder.getPtrTy(),3323 BufferArgAlloca->getName() + ".ascast");3324 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3325 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");3326 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3327 ReduceListArgAlloca, Builder.getPtrTy(),3328 ReduceListArgAlloca->getName() + ".ascast");3329 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3330 LocalReduceList, Builder.getPtrTy(),3331 LocalReduceList->getName() + ".ascast");3332 3333 Builder.CreateStore(BufferArg, BufferArgAddrCast);3334 Builder.CreateStore(IdxArg, IdxArgAddrCast);3335 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);3336 3337 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);3338 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};3339 Type *IndexTy = Builder.getIndexTy(3340 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());3341 for (auto En : enumerate(ReductionInfos)) {3342 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(3343 RedListArrayTy, LocalReduceListAddrCast,3344 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});3345 Value *BufferVD =3346 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);3347 // Global = Buffer.VD[Idx];3348 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(3349 ReductionsBufferTy, BufferVD, 0, En.index());3350 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);3351 }3352 3353 // Call reduce_function(GlobalReduceList, ReduceList)3354 Value *ReduceList =3355 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);3356 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})3357 ->addFnAttr(Attribute::NoUnwind);3358 Builder.CreateRetVoid();3359 Builder.restoreIP(OldIP);3360 return LtGRFunc;3361}3362 3363Function *OpenMPIRBuilder::emitGlobalToListCopyFunction(3364 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,3365 AttributeList FuncAttrs) {3366 OpenMPIRBuilder::InsertPointTy OldIP = Builder.saveIP();3367 LLVMContext &Ctx = M.getContext();3368 FunctionType *FuncTy = FunctionType::get(3369 Builder.getVoidTy(),3370 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},3371 /* IsVarArg */ false);3372 Function *LtGCFunc =3373 Function::Create(FuncTy, GlobalVariable::InternalLinkage,3374 "_omp_reduction_global_to_list_copy_func", &M);3375 LtGCFunc->setAttributes(FuncAttrs);3376 LtGCFunc->addParamAttr(0, Attribute::NoUndef);3377 LtGCFunc->addParamAttr(1, Attribute::NoUndef);3378 LtGCFunc->addParamAttr(2, Attribute::NoUndef);3379 3380 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);3381 Builder.SetInsertPoint(EntryBlock);3382 3383 // Buffer: global reduction buffer.3384 Argument *BufferArg = LtGCFunc->getArg(0);3385 // Idx: index of the buffer.3386 Argument *IdxArg = LtGCFunc->getArg(1);3387 // ReduceList: thread local Reduce list.3388 Argument *ReduceListArg = LtGCFunc->getArg(2);3389 3390 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,3391 BufferArg->getName() + ".addr");3392 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,3393 IdxArg->getName() + ".addr");3394 Value *ReduceListArgAlloca = Builder.CreateAlloca(3395 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");3396 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3397 BufferArgAlloca, Builder.getPtrTy(),3398 BufferArgAlloca->getName() + ".ascast");3399 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3400 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");3401 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3402 ReduceListArgAlloca, Builder.getPtrTy(),3403 ReduceListArgAlloca->getName() + ".ascast");3404 Builder.CreateStore(BufferArg, BufferArgAddrCast);3405 Builder.CreateStore(IdxArg, IdxArgAddrCast);3406 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);3407 3408 Value *LocalReduceList =3409 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);3410 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);3411 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};3412 Type *IndexTy = Builder.getIndexTy(3413 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());3414 for (auto En : enumerate(ReductionInfos)) {3415 const OpenMPIRBuilder::ReductionInfo &RI = En.value();3416 auto *RedListArrayTy =3417 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());3418 // Reduce element = LocalReduceList[i]3419 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(3420 RedListArrayTy, LocalReduceList,3421 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});3422 // elemptr = ((CopyType*)(elemptrptr)) + I3423 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);3424 // Global = Buffer.VD[Idx];3425 Value *BufferVD =3426 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);3427 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(3428 ReductionsBufferTy, BufferVD, 0, En.index());3429 3430 switch (RI.EvaluationKind) {3431 case EvalKind::Scalar: {3432 Value *TargetElement = Builder.CreateLoad(RI.ElementType, GlobValPtr);3433 Builder.CreateStore(TargetElement, ElemPtr);3434 break;3435 }3436 case EvalKind::Complex: {3437 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(3438 RI.ElementType, GlobValPtr, 0, 0, ".realp");3439 Value *SrcReal = Builder.CreateLoad(3440 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");3441 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(3442 RI.ElementType, GlobValPtr, 0, 1, ".imagp");3443 Value *SrcImg = Builder.CreateLoad(3444 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");3445 3446 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(3447 RI.ElementType, ElemPtr, 0, 0, ".realp");3448 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(3449 RI.ElementType, ElemPtr, 0, 1, ".imagp");3450 Builder.CreateStore(SrcReal, DestRealPtr);3451 Builder.CreateStore(SrcImg, DestImgPtr);3452 break;3453 }3454 case EvalKind::Aggregate: {3455 Value *SizeVal =3456 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));3457 Builder.CreateMemCpy(3458 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),3459 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),3460 SizeVal, false);3461 break;3462 }3463 }3464 }3465 3466 Builder.CreateRetVoid();3467 Builder.restoreIP(OldIP);3468 return LtGCFunc;3469}3470 3471Function *OpenMPIRBuilder::emitGlobalToListReduceFunction(3472 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,3473 Type *ReductionsBufferTy, AttributeList FuncAttrs) {3474 OpenMPIRBuilder::InsertPointTy OldIP = Builder.saveIP();3475 LLVMContext &Ctx = M.getContext();3476 auto *FuncTy = FunctionType::get(3477 Builder.getVoidTy(),3478 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},3479 /* IsVarArg */ false);3480 Function *LtGRFunc =3481 Function::Create(FuncTy, GlobalVariable::InternalLinkage,3482 "_omp_reduction_global_to_list_reduce_func", &M);3483 LtGRFunc->setAttributes(FuncAttrs);3484 LtGRFunc->addParamAttr(0, Attribute::NoUndef);3485 LtGRFunc->addParamAttr(1, Attribute::NoUndef);3486 LtGRFunc->addParamAttr(2, Attribute::NoUndef);3487 3488 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);3489 Builder.SetInsertPoint(EntryBlock);3490 3491 // Buffer: global reduction buffer.3492 Argument *BufferArg = LtGRFunc->getArg(0);3493 // Idx: index of the buffer.3494 Argument *IdxArg = LtGRFunc->getArg(1);3495 // ReduceList: thread local Reduce list.3496 Argument *ReduceListArg = LtGRFunc->getArg(2);3497 3498 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,3499 BufferArg->getName() + ".addr");3500 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,3501 IdxArg->getName() + ".addr");3502 Value *ReduceListArgAlloca = Builder.CreateAlloca(3503 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");3504 ArrayType *RedListArrayTy =3505 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());3506 3507 // 1. Build a list of reduction variables.3508 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};3509 Value *LocalReduceList =3510 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");3511 3512 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3513 BufferArgAlloca, Builder.getPtrTy(),3514 BufferArgAlloca->getName() + ".ascast");3515 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3516 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");3517 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3518 ReduceListArgAlloca, Builder.getPtrTy(),3519 ReduceListArgAlloca->getName() + ".ascast");3520 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(3521 LocalReduceList, Builder.getPtrTy(),3522 LocalReduceList->getName() + ".ascast");3523 3524 Builder.CreateStore(BufferArg, BufferArgAddrCast);3525 Builder.CreateStore(IdxArg, IdxArgAddrCast);3526 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);3527 3528 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);3529 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};3530 Type *IndexTy = Builder.getIndexTy(3531 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());3532 for (auto En : enumerate(ReductionInfos)) {3533 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(3534 RedListArrayTy, ReductionList,3535 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});3536 // Global = Buffer.VD[Idx];3537 Value *BufferVD =3538 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);3539 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(3540 ReductionsBufferTy, BufferVD, 0, En.index());3541 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);3542 }3543 3544 // Call reduce_function(ReduceList, GlobalReduceList)3545 Value *ReduceList =3546 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);3547 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})3548 ->addFnAttr(Attribute::NoUnwind);3549 Builder.CreateRetVoid();3550 Builder.restoreIP(OldIP);3551 return LtGRFunc;3552}3553 3554std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {3555 std::string Suffix =3556 createPlatformSpecificName({"omp", "reduction", "reduction_func"});3557 return (Name + Suffix).str();3558}3559 3560Expected<Function *> OpenMPIRBuilder::createReductionFunction(3561 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,3562 ArrayRef<bool> IsByRef, ReductionGenCBKind ReductionGenCBKind,3563 AttributeList FuncAttrs) {3564 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),3565 {Builder.getPtrTy(), Builder.getPtrTy()},3566 /* IsVarArg */ false);3567 std::string Name = getReductionFuncName(ReducerName);3568 Function *ReductionFunc =3569 Function::Create(FuncTy, GlobalVariable::InternalLinkage, Name, &M);3570 ReductionFunc->setAttributes(FuncAttrs);3571 ReductionFunc->addParamAttr(0, Attribute::NoUndef);3572 ReductionFunc->addParamAttr(1, Attribute::NoUndef);3573 BasicBlock *EntryBB =3574 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);3575 Builder.SetInsertPoint(EntryBB);3576 3577 // Need to alloca memory here and deal with the pointers before getting3578 // LHS/RHS pointers out3579 Value *LHSArrayPtr = nullptr;3580 Value *RHSArrayPtr = nullptr;3581 Argument *Arg0 = ReductionFunc->getArg(0);3582 Argument *Arg1 = ReductionFunc->getArg(1);3583 Type *Arg0Type = Arg0->getType();3584 Type *Arg1Type = Arg1->getType();3585 3586 Value *LHSAlloca =3587 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");3588 Value *RHSAlloca =3589 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");3590 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3591 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");3592 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(3593 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");3594 Builder.CreateStore(Arg0, LHSAddrCast);3595 Builder.CreateStore(Arg1, RHSAddrCast);3596 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);3597 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);3598 3599 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());3600 Type *IndexTy = Builder.getIndexTy(3601 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());3602 SmallVector<Value *> LHSPtrs, RHSPtrs;3603 for (auto En : enumerate(ReductionInfos)) {3604 const ReductionInfo &RI = En.value();3605 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(3606 RedArrayTy, RHSArrayPtr,3607 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});3608 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);3609 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(3610 RHSI8Ptr, RI.PrivateVariable->getType(),3611 RHSI8Ptr->getName() + ".ascast");3612 3613 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(3614 RedArrayTy, LHSArrayPtr,3615 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});3616 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);3617 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(3618 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");3619 3620 if (ReductionGenCBKind == ReductionGenCBKind::Clang) {3621 LHSPtrs.emplace_back(LHSPtr);3622 RHSPtrs.emplace_back(RHSPtr);3623 } else {3624 Value *LHS = LHSPtr;3625 Value *RHS = RHSPtr;3626 3627 if (!IsByRef.empty() && !IsByRef[En.index()]) {3628 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);3629 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);3630 }3631 3632 Value *Reduced;3633 InsertPointOrErrorTy AfterIP =3634 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);3635 if (!AfterIP)3636 return AfterIP.takeError();3637 if (!Builder.GetInsertBlock())3638 return ReductionFunc;3639 3640 Builder.restoreIP(*AfterIP);3641 3642 if (!IsByRef.empty() && !IsByRef[En.index()])3643 Builder.CreateStore(Reduced, LHSPtr);3644 }3645 }3646 3647 if (ReductionGenCBKind == ReductionGenCBKind::Clang)3648 for (auto En : enumerate(ReductionInfos)) {3649 unsigned Index = En.index();3650 const ReductionInfo &RI = En.value();3651 Value *LHSFixupPtr, *RHSFixupPtr;3652 Builder.restoreIP(RI.ReductionGenClang(3653 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));3654 3655 // Fix the CallBack code genereated to use the correct Values for the LHS3656 // and RHS3657 LHSFixupPtr->replaceUsesWithIf(3658 LHSPtrs[Index], [ReductionFunc](const Use &U) {3659 return cast<Instruction>(U.getUser())->getParent()->getParent() ==3660 ReductionFunc;3661 });3662 RHSFixupPtr->replaceUsesWithIf(3663 RHSPtrs[Index], [ReductionFunc](const Use &U) {3664 return cast<Instruction>(U.getUser())->getParent()->getParent() ==3665 ReductionFunc;3666 });3667 }3668 3669 Builder.CreateRetVoid();3670 return ReductionFunc;3671}3672 3673static void3674checkReductionInfos(ArrayRef<OpenMPIRBuilder::ReductionInfo> ReductionInfos,3675 bool IsGPU) {3676 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {3677 (void)RI;3678 assert(RI.Variable && "expected non-null variable");3679 assert(RI.PrivateVariable && "expected non-null private variable");3680 assert((RI.ReductionGen || RI.ReductionGenClang) &&3681 "expected non-null reduction generator callback");3682 if (!IsGPU) {3683 assert(3684 RI.Variable->getType() == RI.PrivateVariable->getType() &&3685 "expected variables and their private equivalents to have the same "3686 "type");3687 }3688 assert(RI.Variable->getType()->isPointerTy() &&3689 "expected variables to be pointers");3690 }3691}3692 3693OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createReductionsGPU(3694 const LocationDescription &Loc, InsertPointTy AllocaIP,3695 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,3696 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction,3697 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,3698 unsigned ReductionBufNum, Value *SrcLocInfo) {3699 if (!updateToLocation(Loc))3700 return InsertPointTy();3701 Builder.restoreIP(CodeGenIP);3702 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);3703 LLVMContext &Ctx = M.getContext();3704 3705 // Source location for the ident struct3706 if (!SrcLocInfo) {3707 uint32_t SrcLocStrSize;3708 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);3709 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);3710 }3711 3712 if (ReductionInfos.size() == 0)3713 return Builder.saveIP();3714 3715 BasicBlock *ContinuationBlock = nullptr;3716 if (ReductionGenCBKind != ReductionGenCBKind::Clang) {3717 // Copied code from createReductions3718 BasicBlock *InsertBlock = Loc.IP.getBlock();3719 ContinuationBlock =3720 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");3721 InsertBlock->getTerminator()->eraseFromParent();3722 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());3723 }3724 3725 Function *CurFunc = Builder.GetInsertBlock()->getParent();3726 AttributeList FuncAttrs;3727 AttrBuilder AttrBldr(Ctx);3728 for (auto Attr : CurFunc->getAttributes().getFnAttrs())3729 AttrBldr.addAttribute(Attr);3730 AttrBldr.removeAttribute(Attribute::OptimizeNone);3731 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);3732 3733 CodeGenIP = Builder.saveIP();3734 Expected<Function *> ReductionResult = createReductionFunction(3735 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,3736 ReductionGenCBKind, FuncAttrs);3737 if (!ReductionResult)3738 return ReductionResult.takeError();3739 Function *ReductionFunc = *ReductionResult;3740 Builder.restoreIP(CodeGenIP);3741 3742 // Set the grid value in the config needed for lowering later on3743 if (GridValue.has_value())3744 Config.setGridValue(GridValue.value());3745 else3746 Config.setGridValue(getGridValue(T, ReductionFunc));3747 3748 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),3749 // RedList, shuffle_reduce_func, interwarp_copy_func);3750 // or3751 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);3752 Value *Res;3753 3754 // 1. Build a list of reduction variables.3755 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};3756 auto Size = ReductionInfos.size();3757 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());3758 Type *FuncPtrTy =3759 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());3760 Type *RedArrayTy = ArrayType::get(PtrTy, Size);3761 CodeGenIP = Builder.saveIP();3762 Builder.restoreIP(AllocaIP);3763 Value *ReductionListAlloca =3764 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");3765 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(3766 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");3767 Builder.restoreIP(CodeGenIP);3768 Type *IndexTy = Builder.getIndexTy(3769 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());3770 for (auto En : enumerate(ReductionInfos)) {3771 const ReductionInfo &RI = En.value();3772 Value *ElemPtr = Builder.CreateInBoundsGEP(3773 RedArrayTy, ReductionList,3774 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});3775 3776 Value *PrivateVar = RI.PrivateVariable;3777 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];3778 if (IsByRefElem)3779 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);3780 3781 Value *CastElem =3782 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);3783 Builder.CreateStore(CastElem, ElemPtr);3784 }3785 CodeGenIP = Builder.saveIP();3786 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(3787 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);3788 3789 if (!SarFunc)3790 return SarFunc.takeError();3791 3792 Expected<Function *> CopyResult =3793 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);3794 if (!CopyResult)3795 return CopyResult.takeError();3796 Function *WcFunc = *CopyResult;3797 Builder.restoreIP(CodeGenIP);3798 3799 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);3800 3801 unsigned MaxDataSize = 0;3802 SmallVector<Type *> ReductionTypeArgs;3803 for (auto En : enumerate(ReductionInfos)) {3804 auto Size = M.getDataLayout().getTypeStoreSize(En.value().ElementType);3805 if (Size > MaxDataSize)3806 MaxDataSize = Size;3807 ReductionTypeArgs.emplace_back(En.value().ElementType);3808 }3809 Value *ReductionDataSize =3810 Builder.getInt64(MaxDataSize * ReductionInfos.size());3811 if (!IsTeamsReduction) {3812 Value *SarFuncCast =3813 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);3814 Value *WcFuncCast =3815 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);3816 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,3817 WcFuncCast};3818 Function *Pv2Ptr = getOrCreateRuntimeFunctionPtr(3819 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);3820 Res = createRuntimeFunctionCall(Pv2Ptr, Args);3821 } else {3822 CodeGenIP = Builder.saveIP();3823 StructType *ReductionsBufferTy = StructType::create(3824 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");3825 Function *RedFixedBuferFn = getOrCreateRuntimeFunctionPtr(3826 RuntimeFunction::OMPRTL___kmpc_reduction_get_fixed_buffer);3827 Function *LtGCFunc = emitListToGlobalCopyFunction(3828 ReductionInfos, ReductionsBufferTy, FuncAttrs);3829 Function *LtGRFunc = emitListToGlobalReduceFunction(3830 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs);3831 Function *GtLCFunc = emitGlobalToListCopyFunction(3832 ReductionInfos, ReductionsBufferTy, FuncAttrs);3833 Function *GtLRFunc = emitGlobalToListReduceFunction(3834 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs);3835 Builder.restoreIP(CodeGenIP);3836 3837 Value *KernelTeamsReductionPtr = createRuntimeFunctionCall(3838 RedFixedBuferFn, {}, "_openmp_teams_reductions_buffer_$_$ptr");3839 3840 Value *Args3[] = {SrcLocInfo,3841 KernelTeamsReductionPtr,3842 Builder.getInt32(ReductionBufNum),3843 ReductionDataSize,3844 RL,3845 *SarFunc,3846 WcFunc,3847 LtGCFunc,3848 LtGRFunc,3849 GtLCFunc,3850 GtLRFunc};3851 3852 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(3853 RuntimeFunction::OMPRTL___kmpc_nvptx_teams_reduce_nowait_v2);3854 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);3855 }3856 3857 // 5. Build if (res == 1)3858 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");3859 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");3860 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));3861 Builder.CreateCondBr(Cond, ThenBB, ExitBB);3862 3863 // 6. Build then branch: where we have reduced values in the master3864 // thread in each team.3865 // __kmpc_end_reduce{_nowait}(<gtid>);3866 // break;3867 emitBlock(ThenBB, CurFunc);3868 3869 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);3870 for (auto En : enumerate(ReductionInfos)) {3871 const ReductionInfo &RI = En.value();3872 Type *ValueType = RI.ElementType;3873 Value *RedValue = RI.Variable;3874 Value *RHS =3875 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);3876 3877 if (ReductionGenCBKind == ReductionGenCBKind::Clang) {3878 Value *LHSPtr, *RHSPtr;3879 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),3880 &LHSPtr, &RHSPtr, CurFunc));3881 3882 // Fix the CallBack code genereated to use the correct Values for the LHS3883 // and RHS3884 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {3885 return cast<Instruction>(U.getUser())->getParent()->getParent() ==3886 ReductionFunc;3887 });3888 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {3889 return cast<Instruction>(U.getUser())->getParent()->getParent() ==3890 ReductionFunc;3891 });3892 } else {3893 if (IsByRef.empty() || !IsByRef[En.index()]) {3894 RedValue = Builder.CreateLoad(ValueType, RI.Variable,3895 "red.value." + Twine(En.index()));3896 }3897 Value *PrivateRedValue = Builder.CreateLoad(3898 ValueType, RHS, "red.private.value" + Twine(En.index()));3899 Value *Reduced;3900 InsertPointOrErrorTy AfterIP =3901 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);3902 if (!AfterIP)3903 return AfterIP.takeError();3904 Builder.restoreIP(*AfterIP);3905 3906 if (!IsByRef.empty() && !IsByRef[En.index()])3907 Builder.CreateStore(Reduced, RI.Variable);3908 }3909 }3910 emitBlock(ExitBB, CurFunc);3911 if (ContinuationBlock) {3912 Builder.CreateBr(ContinuationBlock);3913 Builder.SetInsertPoint(ContinuationBlock);3914 }3915 Config.setEmitLLVMUsed();3916 3917 return Builder.saveIP();3918}3919 3920static Function *getFreshReductionFunc(Module &M) {3921 Type *VoidTy = Type::getVoidTy(M.getContext());3922 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());3923 auto *FuncTy =3924 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);3925 return Function::Create(FuncTy, GlobalVariable::InternalLinkage,3926 ".omp.reduction.func", &M);3927}3928 3929static Error populateReductionFunction(3930 Function *ReductionFunc,3931 ArrayRef<OpenMPIRBuilder::ReductionInfo> ReductionInfos,3932 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {3933 Module *Module = ReductionFunc->getParent();3934 BasicBlock *ReductionFuncBlock =3935 BasicBlock::Create(Module->getContext(), "", ReductionFunc);3936 Builder.SetInsertPoint(ReductionFuncBlock);3937 Value *LHSArrayPtr = nullptr;3938 Value *RHSArrayPtr = nullptr;3939 if (IsGPU) {3940 // Need to alloca memory here and deal with the pointers before getting3941 // LHS/RHS pointers out3942 //3943 Argument *Arg0 = ReductionFunc->getArg(0);3944 Argument *Arg1 = ReductionFunc->getArg(1);3945 Type *Arg0Type = Arg0->getType();3946 Type *Arg1Type = Arg1->getType();3947 3948 Value *LHSAlloca =3949 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");3950 Value *RHSAlloca =3951 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");3952 Value *LHSAddrCast =3953 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);3954 Value *RHSAddrCast =3955 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);3956 Builder.CreateStore(Arg0, LHSAddrCast);3957 Builder.CreateStore(Arg1, RHSAddrCast);3958 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);3959 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);3960 } else {3961 LHSArrayPtr = ReductionFunc->getArg(0);3962 RHSArrayPtr = ReductionFunc->getArg(1);3963 }3964 3965 unsigned NumReductions = ReductionInfos.size();3966 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);3967 3968 for (auto En : enumerate(ReductionInfos)) {3969 const OpenMPIRBuilder::ReductionInfo &RI = En.value();3970 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(3971 RedArrayTy, LHSArrayPtr, 0, En.index());3972 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);3973 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(3974 LHSI8Ptr, RI.Variable->getType());3975 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);3976 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(3977 RedArrayTy, RHSArrayPtr, 0, En.index());3978 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);3979 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(3980 RHSI8Ptr, RI.PrivateVariable->getType());3981 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);3982 Value *Reduced;3983 OpenMPIRBuilder::InsertPointOrErrorTy AfterIP =3984 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);3985 if (!AfterIP)3986 return AfterIP.takeError();3987 3988 Builder.restoreIP(*AfterIP);3989 // TODO: Consider flagging an error.3990 if (!Builder.GetInsertBlock())3991 return Error::success();3992 3993 // store is inside of the reduction region when using by-ref3994 if (!IsByRef[En.index()])3995 Builder.CreateStore(Reduced, LHSPtr);3996 }3997 Builder.CreateRetVoid();3998 return Error::success();3999}4000 4001OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createReductions(4002 const LocationDescription &Loc, InsertPointTy AllocaIP,4003 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,4004 bool IsNoWait, bool IsTeamsReduction) {4005 assert(ReductionInfos.size() == IsByRef.size());4006 if (Config.isGPU())4007 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,4008 IsByRef, IsNoWait, IsTeamsReduction);4009 4010 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);4011 4012 if (!updateToLocation(Loc))4013 return InsertPointTy();4014 4015 if (ReductionInfos.size() == 0)4016 return Builder.saveIP();4017 4018 BasicBlock *InsertBlock = Loc.IP.getBlock();4019 BasicBlock *ContinuationBlock =4020 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");4021 InsertBlock->getTerminator()->eraseFromParent();4022 4023 // Create and populate array of type-erased pointers to private reduction4024 // values.4025 unsigned NumReductions = ReductionInfos.size();4026 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);4027 Builder.SetInsertPoint(AllocaIP.getBlock()->getTerminator());4028 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");4029 4030 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());4031 4032 for (auto En : enumerate(ReductionInfos)) {4033 unsigned Index = En.index();4034 const ReductionInfo &RI = En.value();4035 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(4036 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));4037 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);4038 }4039 4040 // Emit a call to the runtime function that orchestrates the reduction.4041 // Declare the reduction function in the process.4042 Type *IndexTy = Builder.getIndexTy(4043 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());4044 Function *Func = Builder.GetInsertBlock()->getParent();4045 Module *Module = Func->getParent();4046 uint32_t SrcLocStrSize;4047 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);4048 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {4049 return RI.AtomicReductionGen;4050 });4051 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,4052 CanGenerateAtomic4053 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE4054 : IdentFlag(0));4055 Value *ThreadId = getOrCreateThreadID(Ident);4056 Constant *NumVariables = Builder.getInt32(NumReductions);4057 const DataLayout &DL = Module->getDataLayout();4058 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);4059 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);4060 Function *ReductionFunc = getFreshReductionFunc(*Module);4061 Value *Lock = getOMPCriticalRegionLock(".reduction");4062 Function *ReduceFunc = getOrCreateRuntimeFunctionPtr(4063 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait4064 : RuntimeFunction::OMPRTL___kmpc_reduce);4065 CallInst *ReduceCall =4066 createRuntimeFunctionCall(ReduceFunc,4067 {Ident, ThreadId, NumVariables, RedArraySize,4068 RedArray, ReductionFunc, Lock},4069 "reduce");4070 4071 // Create final reduction entry blocks for the atomic and non-atomic case.4072 // Emit IR that dispatches control flow to one of the blocks based on the4073 // reduction supporting the atomic mode.4074 BasicBlock *NonAtomicRedBlock =4075 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);4076 BasicBlock *AtomicRedBlock =4077 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);4078 SwitchInst *Switch =4079 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);4080 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);4081 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);4082 4083 // Populate the non-atomic reduction using the elementwise reduction function.4084 // This loads the elements from the global and private variables and reduces4085 // them before storing back the result to the global variable.4086 Builder.SetInsertPoint(NonAtomicRedBlock);4087 for (auto En : enumerate(ReductionInfos)) {4088 const ReductionInfo &RI = En.value();4089 Type *ValueType = RI.ElementType;4090 // We have one less load for by-ref case because that load is now inside of4091 // the reduction region4092 Value *RedValue = RI.Variable;4093 if (!IsByRef[En.index()]) {4094 RedValue = Builder.CreateLoad(ValueType, RI.Variable,4095 "red.value." + Twine(En.index()));4096 }4097 Value *PrivateRedValue =4098 Builder.CreateLoad(ValueType, RI.PrivateVariable,4099 "red.private.value." + Twine(En.index()));4100 Value *Reduced;4101 InsertPointOrErrorTy AfterIP =4102 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);4103 if (!AfterIP)4104 return AfterIP.takeError();4105 Builder.restoreIP(*AfterIP);4106 4107 if (!Builder.GetInsertBlock())4108 return InsertPointTy();4109 // for by-ref case, the load is inside of the reduction region4110 if (!IsByRef[En.index()])4111 Builder.CreateStore(Reduced, RI.Variable);4112 }4113 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(4114 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait4115 : RuntimeFunction::OMPRTL___kmpc_end_reduce);4116 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});4117 Builder.CreateBr(ContinuationBlock);4118 4119 // Populate the atomic reduction using the atomic elementwise reduction4120 // function. There are no loads/stores here because they will be happening4121 // inside the atomic elementwise reduction.4122 Builder.SetInsertPoint(AtomicRedBlock);4123 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {4124 for (const ReductionInfo &RI : ReductionInfos) {4125 InsertPointOrErrorTy AfterIP = RI.AtomicReductionGen(4126 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);4127 if (!AfterIP)4128 return AfterIP.takeError();4129 Builder.restoreIP(*AfterIP);4130 if (!Builder.GetInsertBlock())4131 return InsertPointTy();4132 }4133 Builder.CreateBr(ContinuationBlock);4134 } else {4135 Builder.CreateUnreachable();4136 }4137 4138 // Populate the outlined reduction function using the elementwise reduction4139 // function. Partial values are extracted from the type-erased array of4140 // pointers to private variables.4141 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,4142 IsByRef, /*isGPU=*/false);4143 if (Err)4144 return Err;4145 4146 if (!Builder.GetInsertBlock())4147 return InsertPointTy();4148 4149 Builder.SetInsertPoint(ContinuationBlock);4150 return Builder.saveIP();4151}4152 4153OpenMPIRBuilder::InsertPointOrErrorTy4154OpenMPIRBuilder::createMaster(const LocationDescription &Loc,4155 BodyGenCallbackTy BodyGenCB,4156 FinalizeCallbackTy FiniCB) {4157 if (!updateToLocation(Loc))4158 return Loc.IP;4159 4160 Directive OMPD = Directive::OMPD_master;4161 uint32_t SrcLocStrSize;4162 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);4163 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);4164 Value *ThreadId = getOrCreateThreadID(Ident);4165 Value *Args[] = {Ident, ThreadId};4166 4167 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);4168 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);4169 4170 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);4171 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);4172 4173 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,4174 /*Conditional*/ true, /*hasFinalize*/ true);4175}4176 4177OpenMPIRBuilder::InsertPointOrErrorTy4178OpenMPIRBuilder::createMasked(const LocationDescription &Loc,4179 BodyGenCallbackTy BodyGenCB,4180 FinalizeCallbackTy FiniCB, Value *Filter) {4181 if (!updateToLocation(Loc))4182 return Loc.IP;4183 4184 Directive OMPD = Directive::OMPD_masked;4185 uint32_t SrcLocStrSize;4186 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);4187 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);4188 Value *ThreadId = getOrCreateThreadID(Ident);4189 Value *Args[] = {Ident, ThreadId, Filter};4190 Value *ArgsEnd[] = {Ident, ThreadId};4191 4192 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);4193 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);4194 4195 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);4196 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);4197 4198 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,4199 /*Conditional*/ true, /*hasFinalize*/ true);4200}4201 4202static llvm::CallInst *emitNoUnwindRuntimeCall(IRBuilder<> &Builder,4203 llvm::FunctionCallee Callee,4204 ArrayRef<llvm::Value *> Args,4205 const llvm::Twine &Name) {4206 llvm::CallInst *Call = Builder.CreateCall(4207 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);4208 Call->setDoesNotThrow();4209 return Call;4210}4211 4212// Expects input basic block is dominated by BeforeScanBB.4213// Once Scan directive is encountered, the code after scan directive should be4214// dominated by AfterScanBB. Scan directive splits the code sequence to4215// scan and input phase. Based on whether inclusive or exclusive4216// clause is used in the scan directive and whether input loop or scan loop4217// is lowered, it adds jumps to input and scan phase. First Scan loop is the4218// input loop and second is the scan loop. The code generated handles only4219// inclusive scans now.4220OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createScan(4221 const LocationDescription &Loc, InsertPointTy AllocaIP,4222 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,4223 bool IsInclusive, ScanInfo *ScanRedInfo) {4224 if (ScanRedInfo->OMPFirstScanLoop) {4225 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,4226 ScanVarsType, ScanRedInfo);4227 if (Err)4228 return Err;4229 }4230 if (!updateToLocation(Loc))4231 return Loc.IP;4232 4233 llvm::Value *IV = ScanRedInfo->IV;4234 4235 if (ScanRedInfo->OMPFirstScanLoop) {4236 // Emit buffer[i] = red; at the end of the input phase.4237 for (size_t i = 0; i < ScanVars.size(); i++) {4238 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];4239 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);4240 Type *DestTy = ScanVarsType[i];4241 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");4242 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);4243 4244 Builder.CreateStore(Src, Val);4245 }4246 }4247 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);4248 emitBlock(ScanRedInfo->OMPScanDispatch,4249 Builder.GetInsertBlock()->getParent());4250 4251 if (!ScanRedInfo->OMPFirstScanLoop) {4252 IV = ScanRedInfo->IV;4253 // Emit red = buffer[i]; at the entrance to the scan phase.4254 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.4255 for (size_t i = 0; i < ScanVars.size(); i++) {4256 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];4257 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);4258 Type *DestTy = ScanVarsType[i];4259 Value *SrcPtr =4260 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");4261 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);4262 Builder.CreateStore(Src, ScanVars[i]);4263 }4264 }4265 4266 // TODO: Update it to CreateBr and remove dead blocks4267 llvm::Value *CmpI = Builder.getInt1(true);4268 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {4269 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,4270 ScanRedInfo->OMPAfterScanBlock);4271 } else {4272 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,4273 ScanRedInfo->OMPBeforeScanBlock);4274 }4275 emitBlock(ScanRedInfo->OMPAfterScanBlock,4276 Builder.GetInsertBlock()->getParent());4277 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);4278 return Builder.saveIP();4279}4280 4281Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(4282 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,4283 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {4284 4285 Builder.restoreIP(AllocaIP);4286 // Create the shared pointer at alloca IP.4287 for (size_t i = 0; i < ScanVars.size(); i++) {4288 llvm::Value *BuffPtr =4289 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");4290 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;4291 }4292 4293 // Allocate temporary buffer by master thread4294 auto BodyGenCB = [&](InsertPointTy AllocaIP,4295 InsertPointTy CodeGenIP) -> Error {4296 Builder.restoreIP(CodeGenIP);4297 Value *AllocSpan =4298 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));4299 for (size_t i = 0; i < ScanVars.size(); i++) {4300 Type *IntPtrTy = Builder.getInt32Ty();4301 Constant *Allocsize = ConstantExpr::getSizeOf(ScanVarsType[i]);4302 Allocsize = ConstantExpr::getTruncOrBitCast(Allocsize, IntPtrTy);4303 Value *Buff = Builder.CreateMalloc(IntPtrTy, ScanVarsType[i], Allocsize,4304 AllocSpan, nullptr, "arr");4305 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);4306 }4307 return Error::success();4308 };4309 // TODO: Perform finalization actions for variables. This has to be4310 // called for variables which have destructors/finalizers.4311 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };4312 4313 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());4314 llvm::Value *FilterVal = Builder.getInt32(0);4315 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP =4316 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);4317 4318 if (!AfterIP)4319 return AfterIP.takeError();4320 Builder.restoreIP(*AfterIP);4321 BasicBlock *InputBB = Builder.GetInsertBlock();4322 if (InputBB->getTerminator())4323 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());4324 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);4325 if (!AfterIP)4326 return AfterIP.takeError();4327 Builder.restoreIP(*AfterIP);4328 4329 return Error::success();4330}4331 4332Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(4333 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {4334 auto BodyGenCB = [&](InsertPointTy AllocaIP,4335 InsertPointTy CodeGenIP) -> Error {4336 Builder.restoreIP(CodeGenIP);4337 for (ReductionInfo RedInfo : ReductionInfos) {4338 Value *PrivateVar = RedInfo.PrivateVariable;4339 Value *OrigVar = RedInfo.Variable;4340 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];4341 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);4342 4343 Type *SrcTy = RedInfo.ElementType;4344 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,4345 "arrayOffset");4346 Value *Src = Builder.CreateLoad(SrcTy, Val);4347 4348 Builder.CreateStore(Src, OrigVar);4349 Builder.CreateFree(Buff);4350 }4351 return Error::success();4352 };4353 // TODO: Perform finalization actions for variables. This has to be4354 // called for variables which have destructors/finalizers.4355 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };4356 4357 if (ScanRedInfo->OMPScanFinish->getTerminator())4358 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish->getTerminator());4359 else4360 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);4361 4362 llvm::Value *FilterVal = Builder.getInt32(0);4363 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP =4364 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);4365 4366 if (!AfterIP)4367 return AfterIP.takeError();4368 Builder.restoreIP(*AfterIP);4369 BasicBlock *InputBB = Builder.GetInsertBlock();4370 if (InputBB->getTerminator())4371 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());4372 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);4373 if (!AfterIP)4374 return AfterIP.takeError();4375 Builder.restoreIP(*AfterIP);4376 return Error::success();4377}4378 4379OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitScanReduction(4380 const LocationDescription &Loc,4381 ArrayRef<llvm::OpenMPIRBuilder::ReductionInfo> ReductionInfos,4382 ScanInfo *ScanRedInfo) {4383 4384 if (!updateToLocation(Loc))4385 return Loc.IP;4386 auto BodyGenCB = [&](InsertPointTy AllocaIP,4387 InsertPointTy CodeGenIP) -> Error {4388 Builder.restoreIP(CodeGenIP);4389 Function *CurFn = Builder.GetInsertBlock()->getParent();4390 // for (int k = 0; k <= ceil(log2(n)); ++k)4391 llvm::BasicBlock *LoopBB =4392 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");4393 llvm::BasicBlock *ExitBB =4394 splitBB(Builder, false, "omp.outer.log.scan.exit");4395 llvm::Function *F = llvm::Intrinsic::getOrInsertDeclaration(4396 Builder.GetInsertBlock()->getModule(),4397 (llvm::Intrinsic::ID)llvm::Intrinsic::log2, Builder.getDoubleTy());4398 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();4399 llvm::Value *Arg =4400 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());4401 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");4402 F = llvm::Intrinsic::getOrInsertDeclaration(4403 Builder.GetInsertBlock()->getModule(),4404 (llvm::Intrinsic::ID)llvm::Intrinsic::ceil, Builder.getDoubleTy());4405 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");4406 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());4407 llvm::Value *NMin1 = Builder.CreateNUWSub(4408 ScanRedInfo->Span,4409 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));4410 Builder.SetInsertPoint(InputBB);4411 Builder.CreateBr(LoopBB);4412 emitBlock(LoopBB, CurFn);4413 Builder.SetInsertPoint(LoopBB);4414 4415 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);4416 // size pow2k = 1;4417 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);4418 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),4419 InputBB);4420 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),4421 InputBB);4422 // for (size i = n - 1; i >= 2 ^ k; --i)4423 // tmp[i] op= tmp[i-pow2k];4424 llvm::BasicBlock *InnerLoopBB =4425 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");4426 llvm::BasicBlock *InnerExitBB =4427 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");4428 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);4429 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);4430 emitBlock(InnerLoopBB, CurFn);4431 Builder.SetInsertPoint(InnerLoopBB);4432 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);4433 IVal->addIncoming(NMin1, LoopBB);4434 for (ReductionInfo RedInfo : ReductionInfos) {4435 Value *ReductionVal = RedInfo.PrivateVariable;4436 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];4437 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);4438 Type *DestTy = RedInfo.ElementType;4439 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));4440 Value *LHSPtr =4441 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");4442 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);4443 Value *RHSPtr =4444 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");4445 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);4446 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);4447 llvm::Value *Result;4448 InsertPointOrErrorTy AfterIP =4449 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);4450 if (!AfterIP)4451 return AfterIP.takeError();4452 Builder.CreateStore(Result, LHSPtr);4453 }4454 llvm::Value *NextIVal = Builder.CreateNUWSub(4455 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));4456 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());4457 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);4458 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);4459 emitBlock(InnerExitBB, CurFn);4460 llvm::Value *Next = Builder.CreateNUWAdd(4461 Counter, llvm::ConstantInt::get(Counter->getType(), 1));4462 Counter->addIncoming(Next, Builder.GetInsertBlock());4463 // pow2k <<= 1;4464 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);4465 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());4466 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);4467 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);4468 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());4469 return Error::success();4470 };4471 4472 // TODO: Perform finalization actions for variables. This has to be4473 // called for variables which have destructors/finalizers.4474 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };4475 4476 llvm::Value *FilterVal = Builder.getInt32(0);4477 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP =4478 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);4479 4480 if (!AfterIP)4481 return AfterIP.takeError();4482 Builder.restoreIP(*AfterIP);4483 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);4484 4485 if (!AfterIP)4486 return AfterIP.takeError();4487 Builder.restoreIP(*AfterIP);4488 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);4489 if (Err)4490 return Err;4491 4492 return AfterIP;4493}4494 4495Error OpenMPIRBuilder::emitScanBasedDirectiveIR(4496 llvm::function_ref<Error()> InputLoopGen,4497 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,4498 ScanInfo *ScanRedInfo) {4499 4500 {4501 // Emit loop with input phase:4502 // for (i: 0..<num_iters>) {4503 // <input phase>;4504 // buffer[i] = red;4505 // }4506 ScanRedInfo->OMPFirstScanLoop = true;4507 Error Err = InputLoopGen();4508 if (Err)4509 return Err;4510 }4511 {4512 // Emit loop with scan phase:4513 // for (i: 0..<num_iters>) {4514 // red = buffer[i];4515 // <scan phase>;4516 // }4517 ScanRedInfo->OMPFirstScanLoop = false;4518 Error Err = ScanLoopGen(Builder.saveIP());4519 if (Err)4520 return Err;4521 }4522 return Error::success();4523}4524 4525void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {4526 Function *Fun = Builder.GetInsertBlock()->getParent();4527 ScanRedInfo->OMPScanDispatch =4528 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");4529 ScanRedInfo->OMPAfterScanBlock =4530 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");4531 ScanRedInfo->OMPBeforeScanBlock =4532 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");4533 ScanRedInfo->OMPScanLoopExit =4534 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");4535}4536CanonicalLoopInfo *OpenMPIRBuilder::createLoopSkeleton(4537 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,4538 BasicBlock *PostInsertBefore, const Twine &Name) {4539 Module *M = F->getParent();4540 LLVMContext &Ctx = M->getContext();4541 Type *IndVarTy = TripCount->getType();4542 4543 // Create the basic block structure.4544 BasicBlock *Preheader =4545 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);4546 BasicBlock *Header =4547 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);4548 BasicBlock *Cond =4549 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);4550 BasicBlock *Body =4551 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);4552 BasicBlock *Latch =4553 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);4554 BasicBlock *Exit =4555 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);4556 BasicBlock *After =4557 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);4558 4559 // Use specified DebugLoc for new instructions.4560 Builder.SetCurrentDebugLocation(DL);4561 4562 Builder.SetInsertPoint(Preheader);4563 Builder.CreateBr(Header);4564 4565 Builder.SetInsertPoint(Header);4566 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");4567 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);4568 Builder.CreateBr(Cond);4569 4570 Builder.SetInsertPoint(Cond);4571 Value *Cmp =4572 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");4573 Builder.CreateCondBr(Cmp, Body, Exit);4574 4575 Builder.SetInsertPoint(Body);4576 Builder.CreateBr(Latch);4577 4578 Builder.SetInsertPoint(Latch);4579 Value *Next = Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),4580 "omp_" + Name + ".next", /*HasNUW=*/true);4581 Builder.CreateBr(Header);4582 IndVarPHI->addIncoming(Next, Latch);4583 4584 Builder.SetInsertPoint(Exit);4585 Builder.CreateBr(After);4586 4587 // Remember and return the canonical control flow.4588 LoopInfos.emplace_front();4589 CanonicalLoopInfo *CL = &LoopInfos.front();4590 4591 CL->Header = Header;4592 CL->Cond = Cond;4593 CL->Latch = Latch;4594 CL->Exit = Exit;4595 4596#ifndef NDEBUG4597 CL->assertOK();4598#endif4599 return CL;4600}4601 4602Expected<CanonicalLoopInfo *>4603OpenMPIRBuilder::createCanonicalLoop(const LocationDescription &Loc,4604 LoopBodyGenCallbackTy BodyGenCB,4605 Value *TripCount, const Twine &Name) {4606 BasicBlock *BB = Loc.IP.getBlock();4607 BasicBlock *NextBB = BB->getNextNode();4608 4609 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),4610 NextBB, NextBB, Name);4611 BasicBlock *After = CL->getAfter();4612 4613 // If location is not set, don't connect the loop.4614 if (updateToLocation(Loc)) {4615 // Split the loop at the insertion point: Branch to the preheader and move4616 // every following instruction to after the loop (the After BB). Also, the4617 // new successor is the loop's after block.4618 spliceBB(Builder, After, /*CreateBranch=*/false);4619 Builder.CreateBr(CL->getPreheader());4620 }4621 4622 // Emit the body content. We do it after connecting the loop to the CFG to4623 // avoid that the callback encounters degenerate BBs.4624 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))4625 return Err;4626 4627#ifndef NDEBUG4628 CL->assertOK();4629#endif4630 return CL;4631}4632 4633Expected<ScanInfo *> OpenMPIRBuilder::scanInfoInitialize() {4634 ScanInfos.emplace_front();4635 ScanInfo *Result = &ScanInfos.front();4636 return Result;4637}4638 4639Expected<SmallVector<llvm::CanonicalLoopInfo *>>4640OpenMPIRBuilder::createCanonicalScanLoops(4641 const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB,4642 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,4643 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {4644 LocationDescription ComputeLoc =4645 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;4646 updateToLocation(ComputeLoc);4647 4648 SmallVector<CanonicalLoopInfo *> Result;4649 4650 Value *TripCount = calculateCanonicalLoopTripCount(4651 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);4652 ScanRedInfo->Span = TripCount;4653 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");4654 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);4655 4656 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {4657 Builder.restoreIP(CodeGenIP);4658 ScanRedInfo->IV = IV;4659 createScanBBs(ScanRedInfo);4660 BasicBlock *InputBlock = Builder.GetInsertBlock();4661 Instruction *Terminator = InputBlock->getTerminator();4662 assert(Terminator->getNumSuccessors() == 1);4663 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);4664 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);4665 emitBlock(ScanRedInfo->OMPBeforeScanBlock,4666 Builder.GetInsertBlock()->getParent());4667 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);4668 emitBlock(ScanRedInfo->OMPScanLoopExit,4669 Builder.GetInsertBlock()->getParent());4670 Builder.CreateBr(ContinueBlock);4671 Builder.SetInsertPoint(4672 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());4673 return BodyGenCB(Builder.saveIP(), IV);4674 };4675 4676 const auto &&InputLoopGen = [&]() -> Error {4677 Expected<CanonicalLoopInfo *> LoopInfo = createCanonicalLoop(4678 Builder.saveIP(), BodyGen, Start, Stop, Step, IsSigned, InclusiveStop,4679 ComputeIP, Name, true, ScanRedInfo);4680 if (!LoopInfo)4681 return LoopInfo.takeError();4682 Result.push_back(*LoopInfo);4683 Builder.restoreIP((*LoopInfo)->getAfterIP());4684 return Error::success();4685 };4686 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {4687 Expected<CanonicalLoopInfo *> LoopInfo =4688 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,4689 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);4690 if (!LoopInfo)4691 return LoopInfo.takeError();4692 Result.push_back(*LoopInfo);4693 Builder.restoreIP((*LoopInfo)->getAfterIP());4694 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();4695 return Error::success();4696 };4697 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);4698 if (Err)4699 return Err;4700 return Result;4701}4702 4703Value *OpenMPIRBuilder::calculateCanonicalLoopTripCount(4704 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,4705 bool IsSigned, bool InclusiveStop, const Twine &Name) {4706 4707 // Consider the following difficulties (assuming 8-bit signed integers):4708 // * Adding \p Step to the loop counter which passes \p Stop may overflow:4709 // DO I = 1, 100, 504710 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:4711 // DO I = 100, 0, -1284712 4713 // Start, Stop and Step must be of the same integer type.4714 auto *IndVarTy = cast<IntegerType>(Start->getType());4715 assert(IndVarTy == Stop->getType() && "Stop type mismatch");4716 assert(IndVarTy == Step->getType() && "Step type mismatch");4717 4718 updateToLocation(Loc);4719 4720 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);4721 ConstantInt *One = ConstantInt::get(IndVarTy, 1);4722 4723 // Like Step, but always positive.4724 Value *Incr = Step;4725 4726 // Distance between Start and Stop; always positive.4727 Value *Span;4728 4729 // Condition whether there are no iterations are executed at all, e.g. because4730 // UB < LB.4731 Value *ZeroCmp;4732 4733 if (IsSigned) {4734 // Ensure that increment is positive. If not, negate and invert LB and UB.4735 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);4736 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);4737 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);4738 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);4739 Span = Builder.CreateSub(UB, LB, "", false, true);4740 ZeroCmp = Builder.CreateICmp(4741 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);4742 } else {4743 Span = Builder.CreateSub(Stop, Start, "", true);4744 ZeroCmp = Builder.CreateICmp(4745 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);4746 }4747 4748 Value *CountIfLooping;4749 if (InclusiveStop) {4750 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);4751 } else {4752 // Avoid incrementing past stop since it could overflow.4753 Value *CountIfTwo = Builder.CreateAdd(4754 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);4755 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);4756 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);4757 }4758 4759 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,4760 "omp_" + Name + ".tripcount");4761}4762 4763Expected<CanonicalLoopInfo *> OpenMPIRBuilder::createCanonicalLoop(4764 const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB,4765 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,4766 InsertPointTy ComputeIP, const Twine &Name, bool InScan,4767 ScanInfo *ScanRedInfo) {4768 LocationDescription ComputeLoc =4769 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;4770 4771 Value *TripCount = calculateCanonicalLoopTripCount(4772 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);4773 4774 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {4775 Builder.restoreIP(CodeGenIP);4776 Value *Span = Builder.CreateMul(IV, Step);4777 Value *IndVar = Builder.CreateAdd(Span, Start);4778 if (InScan)4779 ScanRedInfo->IV = IndVar;4780 return BodyGenCB(Builder.saveIP(), IndVar);4781 };4782 LocationDescription LoopLoc =4783 ComputeIP.isSet()4784 ? Loc4785 : LocationDescription(Builder.saveIP(),4786 Builder.getCurrentDebugLocation());4787 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);4788}4789 4790// Returns an LLVM function to call for initializing loop bounds using OpenMP4791// static scheduling for composite `distribute parallel for` depending on4792// `type`. Only i32 and i64 are supported by the runtime. Always interpret4793// integers as unsigned similarly to CanonicalLoopInfo.4794static FunctionCallee4795getKmpcDistForStaticInitForType(Type *Ty, Module &M,4796 OpenMPIRBuilder &OMPBuilder) {4797 unsigned Bitwidth = Ty->getIntegerBitWidth();4798 if (Bitwidth == 32)4799 return OMPBuilder.getOrCreateRuntimeFunction(4800 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);4801 if (Bitwidth == 64)4802 return OMPBuilder.getOrCreateRuntimeFunction(4803 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);4804 llvm_unreachable("unknown OpenMP loop iterator bitwidth");4805}4806 4807// Returns an LLVM function to call for initializing loop bounds using OpenMP4808// static scheduling depending on `type`. Only i32 and i64 are supported by the4809// runtime. Always interpret integers as unsigned similarly to4810// CanonicalLoopInfo.4811static FunctionCallee getKmpcForStaticInitForType(Type *Ty, Module &M,4812 OpenMPIRBuilder &OMPBuilder) {4813 unsigned Bitwidth = Ty->getIntegerBitWidth();4814 if (Bitwidth == 32)4815 return OMPBuilder.getOrCreateRuntimeFunction(4816 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);4817 if (Bitwidth == 64)4818 return OMPBuilder.getOrCreateRuntimeFunction(4819 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);4820 llvm_unreachable("unknown OpenMP loop iterator bitwidth");4821}4822 4823OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(4824 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,4825 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,4826 OMPScheduleType DistScheduleSchedType) {4827 assert(CLI->isValid() && "Requires a valid canonical loop");4828 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&4829 "Require dedicated allocate IP");4830 4831 // Set up the source location value for OpenMP runtime.4832 Builder.restoreIP(CLI->getPreheaderIP());4833 Builder.SetCurrentDebugLocation(DL);4834 4835 uint32_t SrcLocStrSize;4836 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);4837 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize);4838 4839 // Declare useful OpenMP runtime functions.4840 Value *IV = CLI->getIndVar();4841 Type *IVTy = IV->getType();4842 FunctionCallee StaticInit =4843 LoopType == WorksharingLoopType::DistributeForStaticLoop4844 ? getKmpcDistForStaticInitForType(IVTy, M, *this)4845 : getKmpcForStaticInitForType(IVTy, M, *this);4846 FunctionCallee StaticFini =4847 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);4848 4849 // Allocate space for computed loop bounds as expected by the "init" function.4850 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());4851 4852 Type *I32Type = Type::getInt32Ty(M.getContext());4853 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");4854 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");4855 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");4856 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");4857 CLI->setLastIter(PLastIter);4858 4859 // At the end of the preheader, prepare for calling the "init" function by4860 // storing the current loop bounds into the allocated space. A canonical loop4861 // always iterates from 0 to trip-count with step 1. Note that "init" expects4862 // and produces an inclusive upper bound.4863 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());4864 Constant *Zero = ConstantInt::get(IVTy, 0);4865 Constant *One = ConstantInt::get(IVTy, 1);4866 Builder.CreateStore(Zero, PLowerBound);4867 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);4868 Builder.CreateStore(UpperBound, PUpperBound);4869 Builder.CreateStore(One, PStride);4870 4871 Value *ThreadNum = getOrCreateThreadID(SrcLoc);4872 4873 OMPScheduleType SchedType =4874 (LoopType == WorksharingLoopType::DistributeStaticLoop)4875 ? OMPScheduleType::OrderedDistribute4876 : OMPScheduleType::UnorderedStatic;4877 Constant *SchedulingType =4878 ConstantInt::get(I32Type, static_cast<int>(SchedType));4879 4880 // Call the "init" function and update the trip count of the loop with the4881 // value it produced.4882 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,4883 PUpperBound, IVTy, PStride, One, Zero, StaticInit,4884 this](Value *SchedulingType, auto &Builder) {4885 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,4886 PLowerBound, PUpperBound});4887 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {4888 Value *PDistUpperBound =4889 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");4890 Args.push_back(PDistUpperBound);4891 }4892 Args.append({PStride, One, Zero});4893 createRuntimeFunctionCall(StaticInit, Args);4894 };4895 BuildInitCall(SchedulingType, Builder);4896 if (HasDistSchedule &&4897 LoopType != WorksharingLoopType::DistributeStaticLoop) {4898 Constant *DistScheduleSchedType = ConstantInt::get(4899 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));4900 // We want to emit a second init function call for the dist_schedule clause4901 // to the Distribute construct. This should only be done however if a4902 // Workshare Loop is nested within a Distribute Construct4903 BuildInitCall(DistScheduleSchedType, Builder);4904 }4905 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);4906 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);4907 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);4908 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);4909 CLI->setTripCount(TripCount);4910 4911 // Update all uses of the induction variable except the one in the condition4912 // block that compares it with the actual upper bound, and the increment in4913 // the latch block.4914 4915 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {4916 Builder.SetInsertPoint(CLI->getBody(),4917 CLI->getBody()->getFirstInsertionPt());4918 Builder.SetCurrentDebugLocation(DL);4919 return Builder.CreateAdd(OldIV, LowerBound);4920 });4921 4922 // In the "exit" block, call the "fini" function.4923 Builder.SetInsertPoint(CLI->getExit(),4924 CLI->getExit()->getTerminator()->getIterator());4925 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});4926 4927 // Add the barrier if requested.4928 if (NeedsBarrier) {4929 InsertPointOrErrorTy BarrierIP =4930 createBarrier(LocationDescription(Builder.saveIP(), DL),4931 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,4932 /* CheckCancelFlag */ false);4933 if (!BarrierIP)4934 return BarrierIP.takeError();4935 }4936 4937 InsertPointTy AfterIP = CLI->getAfterIP();4938 CLI->invalidate();4939 4940 return AfterIP;4941}4942 4943static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,4944 LoopInfo &LI);4945static void addLoopMetadata(CanonicalLoopInfo *Loop,4946 ArrayRef<Metadata *> Properties);4947 4948static void applyParallelAccessesMetadata(CanonicalLoopInfo *CLI,4949 LLVMContext &Ctx, Loop *Loop,4950 LoopInfo &LoopInfo,4951 SmallVector<Metadata *> &LoopMDList) {4952 SmallSet<BasicBlock *, 8> Reachable;4953 4954 // Get the basic blocks from the loop in which memref instructions4955 // can be found.4956 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,4957 // preferably without running any passes.4958 for (BasicBlock *Block : Loop->getBlocks()) {4959 if (Block == CLI->getCond() || Block == CLI->getHeader())4960 continue;4961 Reachable.insert(Block);4962 }4963 4964 // Add access group metadata to memory-access instructions.4965 MDNode *AccessGroup = MDNode::getDistinct(Ctx, {});4966 for (BasicBlock *BB : Reachable)4967 addAccessGroupMetadata(BB, AccessGroup, LoopInfo);4968 // TODO: If the loop has existing parallel access metadata, have4969 // to combine two lists.4970 LoopMDList.push_back(MDNode::get(4971 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));4972}4973 4974OpenMPIRBuilder::InsertPointOrErrorTy4975OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(4976 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,4977 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,4978 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {4979 assert(CLI->isValid() && "Requires a valid canonical loop");4980 assert(ChunkSize || DistScheduleChunkSize && "Chunk size is required");4981 4982 LLVMContext &Ctx = CLI->getFunction()->getContext();4983 Value *IV = CLI->getIndVar();4984 Value *OrigTripCount = CLI->getTripCount();4985 Type *IVTy = IV->getType();4986 assert(IVTy->getIntegerBitWidth() <= 64 &&4987 "Max supported tripcount bitwidth is 64 bits");4988 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)4989 : Type::getInt64Ty(Ctx);4990 Type *I32Type = Type::getInt32Ty(M.getContext());4991 Constant *Zero = ConstantInt::get(InternalIVTy, 0);4992 Constant *One = ConstantInt::get(InternalIVTy, 1);4993 4994 Function *F = CLI->getFunction();4995 FunctionAnalysisManager FAM;4996 FAM.registerPass([]() { return DominatorTreeAnalysis(); });4997 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });4998 LoopAnalysis LIA;4999 LoopInfo &&LI = LIA.run(*F, FAM);5000 Loop *L = LI.getLoopFor(CLI->getHeader());5001 SmallVector<Metadata *> LoopMDList;5002 if (ChunkSize || DistScheduleChunkSize)5003 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);5004 addLoopMetadata(CLI, LoopMDList);5005 5006 // Declare useful OpenMP runtime functions.5007 FunctionCallee StaticInit =5008 getKmpcForStaticInitForType(InternalIVTy, M, *this);5009 FunctionCallee StaticFini =5010 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);5011 5012 // Allocate space for computed loop bounds as expected by the "init" function.5013 Builder.restoreIP(AllocaIP);5014 Builder.SetCurrentDebugLocation(DL);5015 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");5016 Value *PLowerBound =5017 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");5018 Value *PUpperBound =5019 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");5020 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");5021 CLI->setLastIter(PLastIter);5022 5023 // Set up the source location value for the OpenMP runtime.5024 Builder.restoreIP(CLI->getPreheaderIP());5025 Builder.SetCurrentDebugLocation(DL);5026 5027 // TODO: Detect overflow in ubsan or max-out with current tripcount.5028 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(5029 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");5030 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(5031 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,5032 "distschedulechunksize");5033 Value *CastedTripCount =5034 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");5035 5036 Constant *SchedulingType =5037 ConstantInt::get(I32Type, static_cast<int>(SchedType));5038 Constant *DistSchedulingType =5039 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));5040 Builder.CreateStore(Zero, PLowerBound);5041 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);5042 Builder.CreateStore(OrigUpperBound, PUpperBound);5043 Builder.CreateStore(One, PStride);5044 5045 // Call the "init" function and update the trip count of the loop with the5046 // value it produced.5047 uint32_t SrcLocStrSize;5048 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);5049 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize);5050 Value *ThreadNum = getOrCreateThreadID(SrcLoc);5051 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,5052 PUpperBound, PStride, One,5053 this](Value *SchedulingType, Value *ChunkSize,5054 auto &Builder) {5055 createRuntimeFunctionCall(5056 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,5057 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,5058 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,5059 /*pstride=*/PStride, /*incr=*/One,5060 /*chunk=*/ChunkSize});5061 };5062 BuildInitCall(SchedulingType, CastedChunkSize, Builder);5063 if (DistScheduleSchedType != OMPScheduleType::None &&5064 SchedType != OMPScheduleType::OrderedDistributeChunked &&5065 SchedType != OMPScheduleType::OrderedDistribute) {5066 // We want to emit a second init function call for the dist_schedule clause5067 // to the Distribute construct. This should only be done however if a5068 // Workshare Loop is nested within a Distribute Construct5069 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);5070 }5071 5072 // Load values written by the "init" function.5073 Value *FirstChunkStart =5074 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");5075 Value *FirstChunkStop =5076 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");5077 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);5078 Value *ChunkRange =5079 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");5080 Value *NextChunkStride =5081 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");5082 5083 // Create outer "dispatch" loop for enumerating the chunks.5084 BasicBlock *DispatchEnter = splitBB(Builder, true);5085 Value *DispatchCounter;5086 5087 // It is safe to assume this didn't return an error because the callback5088 // passed into createCanonicalLoop is the only possible error source, and it5089 // always returns success.5090 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(5091 {Builder.saveIP(), DL},5092 [&](InsertPointTy BodyIP, Value *Counter) {5093 DispatchCounter = Counter;5094 return Error::success();5095 },5096 FirstChunkStart, CastedTripCount, NextChunkStride,5097 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},5098 "dispatch"));5099 5100 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to5101 // not have to preserve the canonical invariant.5102 BasicBlock *DispatchBody = DispatchCLI->getBody();5103 BasicBlock *DispatchLatch = DispatchCLI->getLatch();5104 BasicBlock *DispatchExit = DispatchCLI->getExit();5105 BasicBlock *DispatchAfter = DispatchCLI->getAfter();5106 DispatchCLI->invalidate();5107 5108 // Rewire the original loop to become the chunk loop inside the dispatch loop.5109 redirectTo(DispatchAfter, CLI->getAfter(), DL);5110 redirectTo(CLI->getExit(), DispatchLatch, DL);5111 redirectTo(DispatchBody, DispatchEnter, DL);5112 5113 // Prepare the prolog of the chunk loop.5114 Builder.restoreIP(CLI->getPreheaderIP());5115 Builder.SetCurrentDebugLocation(DL);5116 5117 // Compute the number of iterations of the chunk loop.5118 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());5119 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);5120 Value *IsLastChunk =5121 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");5122 Value *CountUntilOrigTripCount =5123 Builder.CreateSub(CastedTripCount, DispatchCounter);5124 Value *ChunkTripCount = Builder.CreateSelect(5125 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");5126 Value *BackcastedChunkTC =5127 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");5128 CLI->setTripCount(BackcastedChunkTC);5129 5130 // Update all uses of the induction variable except the one in the condition5131 // block that compares it with the actual upper bound, and the increment in5132 // the latch block.5133 Value *BackcastedDispatchCounter =5134 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");5135 CLI->mapIndVar([&](Instruction *) -> Value * {5136 Builder.restoreIP(CLI->getBodyIP());5137 return Builder.CreateAdd(IV, BackcastedDispatchCounter);5138 });5139 5140 // In the "exit" block, call the "fini" function.5141 Builder.SetInsertPoint(DispatchExit, DispatchExit->getFirstInsertionPt());5142 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});5143 5144 // Add the barrier if requested.5145 if (NeedsBarrier) {5146 InsertPointOrErrorTy AfterIP =5147 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,5148 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);5149 if (!AfterIP)5150 return AfterIP.takeError();5151 }5152 5153#ifndef NDEBUG5154 // Even though we currently do not support applying additional methods to it,5155 // the chunk loop should remain a canonical loop.5156 CLI->assertOK();5157#endif5158 5159 return InsertPointTy(DispatchAfter, DispatchAfter->getFirstInsertionPt());5160}5161 5162// Returns an LLVM function to call for executing an OpenMP static worksharing5163// for loop depending on `type`. Only i32 and i64 are supported by the runtime.5164// Always interpret integers as unsigned similarly to CanonicalLoopInfo.5165static FunctionCallee5166getKmpcForStaticLoopForType(Type *Ty, OpenMPIRBuilder *OMPBuilder,5167 WorksharingLoopType LoopType) {5168 unsigned Bitwidth = Ty->getIntegerBitWidth();5169 Module &M = OMPBuilder->M;5170 switch (LoopType) {5171 case WorksharingLoopType::ForStaticLoop:5172 if (Bitwidth == 32)5173 return OMPBuilder->getOrCreateRuntimeFunction(5174 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);5175 if (Bitwidth == 64)5176 return OMPBuilder->getOrCreateRuntimeFunction(5177 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);5178 break;5179 case WorksharingLoopType::DistributeStaticLoop:5180 if (Bitwidth == 32)5181 return OMPBuilder->getOrCreateRuntimeFunction(5182 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);5183 if (Bitwidth == 64)5184 return OMPBuilder->getOrCreateRuntimeFunction(5185 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);5186 break;5187 case WorksharingLoopType::DistributeForStaticLoop:5188 if (Bitwidth == 32)5189 return OMPBuilder->getOrCreateRuntimeFunction(5190 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);5191 if (Bitwidth == 64)5192 return OMPBuilder->getOrCreateRuntimeFunction(5193 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);5194 break;5195 }5196 if (Bitwidth != 32 && Bitwidth != 64) {5197 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");5198 }5199 llvm_unreachable("Unknown type of OpenMP worksharing loop");5200}5201 5202// Inserts a call to proper OpenMP Device RTL function which handles5203// loop worksharing.5204static void createTargetLoopWorkshareCall(OpenMPIRBuilder *OMPBuilder,5205 WorksharingLoopType LoopType,5206 BasicBlock *InsertBlock, Value *Ident,5207 Value *LoopBodyArg, Value *TripCount,5208 Function &LoopBodyFn, bool NoLoop) {5209 Type *TripCountTy = TripCount->getType();5210 Module &M = OMPBuilder->M;5211 IRBuilder<> &Builder = OMPBuilder->Builder;5212 FunctionCallee RTLFn =5213 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);5214 SmallVector<Value *, 8> RealArgs;5215 RealArgs.push_back(Ident);5216 RealArgs.push_back(&LoopBodyFn);5217 RealArgs.push_back(LoopBodyArg);5218 RealArgs.push_back(TripCount);5219 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {5220 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));5221 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));5222 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});5223 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);5224 return;5225 }5226 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(5227 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);5228 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});5229 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});5230 5231 RealArgs.push_back(5232 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));5233 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));5234 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {5235 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));5236 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));5237 } else {5238 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));5239 }5240 5241 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);5242}5243 5244static void workshareLoopTargetCallback(5245 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,5246 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,5247 WorksharingLoopType LoopType, bool NoLoop) {5248 IRBuilder<> &Builder = OMPIRBuilder->Builder;5249 BasicBlock *Preheader = CLI->getPreheader();5250 Value *TripCount = CLI->getTripCount();5251 5252 // After loop body outling, the loop body contains only set up5253 // of loop body argument structure and the call to the outlined5254 // loop body function. Firstly, we need to move setup of loop body args5255 // into loop preheader.5256 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),5257 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));5258 5259 // The next step is to remove the whole loop. We do not it need anymore.5260 // That's why make an unconditional branch from loop preheader to loop5261 // exit block5262 Builder.restoreIP({Preheader, Preheader->end()});5263 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());5264 Preheader->getTerminator()->eraseFromParent();5265 Builder.CreateBr(CLI->getExit());5266 5267 // Delete dead loop blocks5268 OpenMPIRBuilder::OutlineInfo CleanUpInfo;5269 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;5270 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;5271 CleanUpInfo.EntryBB = CLI->getHeader();5272 CleanUpInfo.ExitBB = CLI->getExit();5273 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);5274 DeleteDeadBlocks(BlocksToBeRemoved);5275 5276 // Find the instruction which corresponds to loop body argument structure5277 // and remove the call to loop body function instruction.5278 Value *LoopBodyArg;5279 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();5280 assert(OutlinedFnUser &&5281 "Expected unique undroppable user of outlined function");5282 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);5283 assert(OutlinedFnCallInstruction && "Expected outlined function call");5284 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&5285 "Expected outlined function call to be located in loop preheader");5286 // Check in case no argument structure has been passed.5287 if (OutlinedFnCallInstruction->arg_size() > 1)5288 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);5289 else5290 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());5291 OutlinedFnCallInstruction->eraseFromParent();5292 5293 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,5294 LoopBodyArg, TripCount, OutlinedFn, NoLoop);5295 5296 for (auto &ToBeDeletedItem : ToBeDeleted)5297 ToBeDeletedItem->eraseFromParent();5298 CLI->invalidate();5299}5300 5301OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::applyWorkshareLoopTarget(5302 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,5303 WorksharingLoopType LoopType, bool NoLoop) {5304 uint32_t SrcLocStrSize;5305 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);5306 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);5307 5308 OutlineInfo OI;5309 OI.OuterAllocaBB = CLI->getPreheader();5310 Function *OuterFn = CLI->getPreheader()->getParent();5311 5312 // Instructions which need to be deleted at the end of code generation5313 SmallVector<Instruction *, 4> ToBeDeleted;5314 5315 OI.OuterAllocaBB = AllocaIP.getBlock();5316 5317 // Mark the body loop as region which needs to be extracted5318 OI.EntryBB = CLI->getBody();5319 OI.ExitBB = CLI->getLatch()->splitBasicBlock(CLI->getLatch()->begin(),5320 "omp.prelatch", true);5321 5322 // Prepare loop body for extraction5323 Builder.restoreIP({CLI->getPreheader(), CLI->getPreheader()->begin()});5324 5325 // Insert new loop counter variable which will be used only in loop5326 // body.5327 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");5328 Instruction *NewLoopCntLoad =5329 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);5330 // New loop counter instructions are redundant in the loop preheader when5331 // code generation for workshare loop is finshed. That's why mark them as5332 // ready for deletion.5333 ToBeDeleted.push_back(NewLoopCntLoad);5334 ToBeDeleted.push_back(NewLoopCnt);5335 5336 // Analyse loop body region. Find all input variables which are used inside5337 // loop body region.5338 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;5339 SmallVector<BasicBlock *, 32> Blocks;5340 OI.collectBlocks(ParallelRegionBlockSet, Blocks);5341 5342 CodeExtractorAnalysisCache CEAC(*OuterFn);5343 CodeExtractor Extractor(Blocks,5344 /* DominatorTree */ nullptr,5345 /* AggregateArgs */ true,5346 /* BlockFrequencyInfo */ nullptr,5347 /* BranchProbabilityInfo */ nullptr,5348 /* AssumptionCache */ nullptr,5349 /* AllowVarArgs */ true,5350 /* AllowAlloca */ true,5351 /* AllocationBlock */ CLI->getPreheader(),5352 /* Suffix */ ".omp_wsloop",5353 /* AggrArgsIn0AddrSpace */ true);5354 5355 BasicBlock *CommonExit = nullptr;5356 SetVector<Value *> SinkingCands, HoistingCands;5357 5358 // Find allocas outside the loop body region which are used inside loop5359 // body5360 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);5361 5362 // We need to model loop body region as the function f(cnt, loop_arg).5363 // That's why we replace loop induction variable by the new counter5364 // which will be one of loop body function argument5365 SmallVector<User *> Users(CLI->getIndVar()->user_begin(),5366 CLI->getIndVar()->user_end());5367 for (auto Use : Users) {5368 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {5369 if (ParallelRegionBlockSet.count(Inst->getParent())) {5370 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);5371 }5372 }5373 }5374 // Make sure that loop counter variable is not merged into loop body5375 // function argument structure and it is passed as separate variable5376 OI.ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);5377 5378 // PostOutline CB is invoked when loop body function is outlined and5379 // loop body is replaced by call to outlined function. We need to add5380 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl5381 // function will handle loop control logic.5382 //5383 OI.PostOutlineCB = [=, ToBeDeletedVec =5384 std::move(ToBeDeleted)](Function &OutlinedFn) {5385 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,5386 LoopType, NoLoop);5387 };5388 addOutlineInfo(std::move(OI));5389 return CLI->getAfterIP();5390}5391 5392OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyWorkshareLoop(5393 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,5394 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,5395 bool HasSimdModifier, bool HasMonotonicModifier,5396 bool HasNonmonotonicModifier, bool HasOrderedClause,5397 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,5398 Value *DistScheduleChunkSize) {5399 if (Config.isTargetDevice())5400 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NoLoop);5401 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(5402 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,5403 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);5404 5405 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==5406 OMPScheduleType::ModifierOrdered;5407 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;5408 if (HasDistSchedule) {5409 DistScheduleSchedType = DistScheduleChunkSize5410 ? OMPScheduleType::OrderedDistributeChunked5411 : OMPScheduleType::OrderedDistribute;5412 }5413 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {5414 case OMPScheduleType::BaseStatic:5415 case OMPScheduleType::BaseDistribute:5416 assert(!ChunkSize || !DistScheduleChunkSize &&5417 "No chunk size with static-chunked schedule");5418 if (IsOrdered && !HasDistSchedule)5419 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,5420 NeedsBarrier, ChunkSize);5421 // FIXME: Monotonicity ignored?5422 if (DistScheduleChunkSize)5423 return applyStaticChunkedWorkshareLoop(5424 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,5425 DistScheduleChunkSize, DistScheduleSchedType);5426 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,5427 HasDistSchedule);5428 5429 case OMPScheduleType::BaseStaticChunked:5430 case OMPScheduleType::BaseDistributeChunked:5431 if (IsOrdered && !HasDistSchedule)5432 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,5433 NeedsBarrier, ChunkSize);5434 // FIXME: Monotonicity ignored?5435 return applyStaticChunkedWorkshareLoop(5436 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,5437 DistScheduleChunkSize, DistScheduleSchedType);5438 5439 case OMPScheduleType::BaseRuntime:5440 case OMPScheduleType::BaseAuto:5441 case OMPScheduleType::BaseGreedy:5442 case OMPScheduleType::BaseBalanced:5443 case OMPScheduleType::BaseSteal:5444 case OMPScheduleType::BaseGuidedSimd:5445 case OMPScheduleType::BaseRuntimeSimd:5446 assert(!ChunkSize &&5447 "schedule type does not support user-defined chunk sizes");5448 [[fallthrough]];5449 case OMPScheduleType::BaseDynamicChunked:5450 case OMPScheduleType::BaseGuidedChunked:5451 case OMPScheduleType::BaseGuidedIterativeChunked:5452 case OMPScheduleType::BaseGuidedAnalyticalChunked:5453 case OMPScheduleType::BaseStaticBalancedChunked:5454 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,5455 NeedsBarrier, ChunkSize);5456 5457 default:5458 llvm_unreachable("Unknown/unimplemented schedule kind");5459 }5460}5461 5462/// Returns an LLVM function to call for initializing loop bounds using OpenMP5463/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by5464/// the runtime. Always interpret integers as unsigned similarly to5465/// CanonicalLoopInfo.5466static FunctionCallee5467getKmpcForDynamicInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder) {5468 unsigned Bitwidth = Ty->getIntegerBitWidth();5469 if (Bitwidth == 32)5470 return OMPBuilder.getOrCreateRuntimeFunction(5471 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);5472 if (Bitwidth == 64)5473 return OMPBuilder.getOrCreateRuntimeFunction(5474 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);5475 llvm_unreachable("unknown OpenMP loop iterator bitwidth");5476}5477 5478/// Returns an LLVM function to call for updating the next loop using OpenMP5479/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by5480/// the runtime. Always interpret integers as unsigned similarly to5481/// CanonicalLoopInfo.5482static FunctionCallee5483getKmpcForDynamicNextForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder) {5484 unsigned Bitwidth = Ty->getIntegerBitWidth();5485 if (Bitwidth == 32)5486 return OMPBuilder.getOrCreateRuntimeFunction(5487 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);5488 if (Bitwidth == 64)5489 return OMPBuilder.getOrCreateRuntimeFunction(5490 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);5491 llvm_unreachable("unknown OpenMP loop iterator bitwidth");5492}5493 5494/// Returns an LLVM function to call for finalizing the dynamic loop using5495/// depending on `type`. Only i32 and i64 are supported by the runtime. Always5496/// interpret integers as unsigned similarly to CanonicalLoopInfo.5497static FunctionCallee5498getKmpcForDynamicFiniForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder) {5499 unsigned Bitwidth = Ty->getIntegerBitWidth();5500 if (Bitwidth == 32)5501 return OMPBuilder.getOrCreateRuntimeFunction(5502 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);5503 if (Bitwidth == 64)5504 return OMPBuilder.getOrCreateRuntimeFunction(5505 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);5506 llvm_unreachable("unknown OpenMP loop iterator bitwidth");5507}5508 5509OpenMPIRBuilder::InsertPointOrErrorTy5510OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,5511 InsertPointTy AllocaIP,5512 OMPScheduleType SchedType,5513 bool NeedsBarrier, Value *Chunk) {5514 assert(CLI->isValid() && "Requires a valid canonical loop");5515 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&5516 "Require dedicated allocate IP");5517 assert(isValidWorkshareLoopScheduleType(SchedType) &&5518 "Require valid schedule type");5519 5520 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==5521 OMPScheduleType::ModifierOrdered;5522 5523 // Set up the source location value for OpenMP runtime.5524 Builder.SetCurrentDebugLocation(DL);5525 5526 uint32_t SrcLocStrSize;5527 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);5528 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize);5529 5530 // Declare useful OpenMP runtime functions.5531 Value *IV = CLI->getIndVar();5532 Type *IVTy = IV->getType();5533 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);5534 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);5535 5536 // Allocate space for computed loop bounds as expected by the "init" function.5537 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());5538 Type *I32Type = Type::getInt32Ty(M.getContext());5539 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");5540 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");5541 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");5542 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");5543 CLI->setLastIter(PLastIter);5544 5545 // At the end of the preheader, prepare for calling the "init" function by5546 // storing the current loop bounds into the allocated space. A canonical loop5547 // always iterates from 0 to trip-count with step 1. Note that "init" expects5548 // and produces an inclusive upper bound.5549 BasicBlock *PreHeader = CLI->getPreheader();5550 Builder.SetInsertPoint(PreHeader->getTerminator());5551 Constant *One = ConstantInt::get(IVTy, 1);5552 Builder.CreateStore(One, PLowerBound);5553 Value *UpperBound = CLI->getTripCount();5554 Builder.CreateStore(UpperBound, PUpperBound);5555 Builder.CreateStore(One, PStride);5556 5557 BasicBlock *Header = CLI->getHeader();5558 BasicBlock *Exit = CLI->getExit();5559 BasicBlock *Cond = CLI->getCond();5560 BasicBlock *Latch = CLI->getLatch();5561 InsertPointTy AfterIP = CLI->getAfterIP();5562 5563 // The CLI will be "broken" in the code below, as the loop is no longer5564 // a valid canonical loop.5565 5566 if (!Chunk)5567 Chunk = One;5568 5569 Value *ThreadNum = getOrCreateThreadID(SrcLoc);5570 5571 Constant *SchedulingType =5572 ConstantInt::get(I32Type, static_cast<int>(SchedType));5573 5574 // Call the "init" function.5575 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,5576 /* LowerBound */ One, UpperBound,5577 /* step */ One, Chunk});5578 5579 // An outer loop around the existing one.5580 BasicBlock *OuterCond = BasicBlock::Create(5581 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",5582 PreHeader->getParent());5583 // This needs to be 32-bit always, so can't use the IVTy Zero above.5584 Builder.SetInsertPoint(OuterCond, OuterCond->getFirstInsertionPt());5585 Value *Res = createRuntimeFunctionCall(5586 DynamicNext,5587 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});5588 Constant *Zero32 = ConstantInt::get(I32Type, 0);5589 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);5590 Value *LowerBound =5591 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");5592 Builder.CreateCondBr(MoreWork, Header, Exit);5593 5594 // Change PHI-node in loop header to use outer cond rather than preheader,5595 // and set IV to the LowerBound.5596 Instruction *Phi = &Header->front();5597 auto *PI = cast<PHINode>(Phi);5598 PI->setIncomingBlock(0, OuterCond);5599 PI->setIncomingValue(0, LowerBound);5600 5601 // Then set the pre-header to jump to the OuterCond5602 Instruction *Term = PreHeader->getTerminator();5603 auto *Br = cast<BranchInst>(Term);5604 Br->setSuccessor(0, OuterCond);5605 5606 // Modify the inner condition:5607 // * Use the UpperBound returned from the DynamicNext call.5608 // * jump to the loop outer loop when done with one of the inner loops.5609 Builder.SetInsertPoint(Cond, Cond->getFirstInsertionPt());5610 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");5611 Instruction *Comp = &*Builder.GetInsertPoint();5612 auto *CI = cast<CmpInst>(Comp);5613 CI->setOperand(1, UpperBound);5614 // Redirect the inner exit to branch to outer condition.5615 Instruction *Branch = &Cond->back();5616 auto *BI = cast<BranchInst>(Branch);5617 assert(BI->getSuccessor(1) == Exit);5618 BI->setSuccessor(1, OuterCond);5619 5620 // Call the "fini" function if "ordered" is present in wsloop directive.5621 if (Ordered) {5622 Builder.SetInsertPoint(&Latch->back());5623 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);5624 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});5625 }5626 5627 // Add the barrier if requested.5628 if (NeedsBarrier) {5629 Builder.SetInsertPoint(&Exit->back());5630 InsertPointOrErrorTy BarrierIP =5631 createBarrier(LocationDescription(Builder.saveIP(), DL),5632 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,5633 /* CheckCancelFlag */ false);5634 if (!BarrierIP)5635 return BarrierIP.takeError();5636 }5637 5638 CLI->invalidate();5639 return AfterIP;5640}5641 5642/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,5643/// after this \p OldTarget will be orphaned.5644static void redirectAllPredecessorsTo(BasicBlock *OldTarget,5645 BasicBlock *NewTarget, DebugLoc DL) {5646 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))5647 redirectTo(Pred, NewTarget, DL);5648}5649 5650/// Determine which blocks in \p BBs are reachable from outside and remove the5651/// ones that are not reachable from the function.5652static void removeUnusedBlocksFromParent(ArrayRef<BasicBlock *> BBs) {5653 SmallPtrSet<BasicBlock *, 6> BBsToErase(llvm::from_range, BBs);5654 auto HasRemainingUses = [&BBsToErase](BasicBlock *BB) {5655 for (Use &U : BB->uses()) {5656 auto *UseInst = dyn_cast<Instruction>(U.getUser());5657 if (!UseInst)5658 continue;5659 if (BBsToErase.count(UseInst->getParent()))5660 continue;5661 return true;5662 }5663 return false;5664 };5665 5666 while (BBsToErase.remove_if(HasRemainingUses)) {5667 // Try again if anything was removed.5668 }5669 5670 SmallVector<BasicBlock *, 7> BBVec(BBsToErase.begin(), BBsToErase.end());5671 DeleteDeadBlocks(BBVec);5672}5673 5674CanonicalLoopInfo *5675OpenMPIRBuilder::collapseLoops(DebugLoc DL, ArrayRef<CanonicalLoopInfo *> Loops,5676 InsertPointTy ComputeIP) {5677 assert(Loops.size() >= 1 && "At least one loop required");5678 size_t NumLoops = Loops.size();5679 5680 // Nothing to do if there is already just one loop.5681 if (NumLoops == 1)5682 return Loops.front();5683 5684 CanonicalLoopInfo *Outermost = Loops.front();5685 CanonicalLoopInfo *Innermost = Loops.back();5686 BasicBlock *OrigPreheader = Outermost->getPreheader();5687 BasicBlock *OrigAfter = Outermost->getAfter();5688 Function *F = OrigPreheader->getParent();5689 5690 // Loop control blocks that may become orphaned later.5691 SmallVector<BasicBlock *, 12> OldControlBBs;5692 OldControlBBs.reserve(6 * Loops.size());5693 for (CanonicalLoopInfo *Loop : Loops)5694 Loop->collectControlBlocks(OldControlBBs);5695 5696 // Setup the IRBuilder for inserting the trip count computation.5697 Builder.SetCurrentDebugLocation(DL);5698 if (ComputeIP.isSet())5699 Builder.restoreIP(ComputeIP);5700 else5701 Builder.restoreIP(Outermost->getPreheaderIP());5702 5703 // Derive the collapsed' loop trip count.5704 // TODO: Find common/largest indvar type.5705 Value *CollapsedTripCount = nullptr;5706 for (CanonicalLoopInfo *L : Loops) {5707 assert(L->isValid() &&5708 "All loops to collapse must be valid canonical loops");5709 Value *OrigTripCount = L->getTripCount();5710 if (!CollapsedTripCount) {5711 CollapsedTripCount = OrigTripCount;5712 continue;5713 }5714 5715 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.5716 CollapsedTripCount =5717 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);5718 }5719 5720 // Create the collapsed loop control flow.5721 CanonicalLoopInfo *Result =5722 createLoopSkeleton(DL, CollapsedTripCount, F,5723 OrigPreheader->getNextNode(), OrigAfter, "collapsed");5724 5725 // Build the collapsed loop body code.5726 // Start with deriving the input loop induction variables from the collapsed5727 // one, using a divmod scheme. To preserve the original loops' order, the5728 // innermost loop use the least significant bits.5729 Builder.restoreIP(Result->getBodyIP());5730 5731 Value *Leftover = Result->getIndVar();5732 SmallVector<Value *> NewIndVars;5733 NewIndVars.resize(NumLoops);5734 for (int i = NumLoops - 1; i >= 1; --i) {5735 Value *OrigTripCount = Loops[i]->getTripCount();5736 5737 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);5738 NewIndVars[i] = NewIndVar;5739 5740 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);5741 }5742 // Outermost loop gets all the remaining bits.5743 NewIndVars[0] = Leftover;5744 5745 // Construct the loop body control flow.5746 // We progressively construct the branch structure following in direction of5747 // the control flow, from the leading in-between code, the loop nest body, the5748 // trailing in-between code, and rejoining the collapsed loop's latch.5749 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If5750 // the ContinueBlock is set, continue with that block. If ContinuePred, use5751 // its predecessors as sources.5752 BasicBlock *ContinueBlock = Result->getBody();5753 BasicBlock *ContinuePred = nullptr;5754 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,5755 BasicBlock *NextSrc) {5756 if (ContinueBlock)5757 redirectTo(ContinueBlock, Dest, DL);5758 else5759 redirectAllPredecessorsTo(ContinuePred, Dest, DL);5760 5761 ContinueBlock = nullptr;5762 ContinuePred = NextSrc;5763 };5764 5765 // The code before the nested loop of each level.5766 // Because we are sinking it into the nest, it will be executed more often5767 // that the original loop. More sophisticated schemes could keep track of what5768 // the in-between code is and instantiate it only once per thread.5769 for (size_t i = 0; i < NumLoops - 1; ++i)5770 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());5771 5772 // Connect the loop nest body.5773 ContinueWith(Innermost->getBody(), Innermost->getLatch());5774 5775 // The code after the nested loop at each level.5776 for (size_t i = NumLoops - 1; i > 0; --i)5777 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());5778 5779 // Connect the finished loop to the collapsed loop latch.5780 ContinueWith(Result->getLatch(), nullptr);5781 5782 // Replace the input loops with the new collapsed loop.5783 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);5784 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);5785 5786 // Replace the input loop indvars with the derived ones.5787 for (size_t i = 0; i < NumLoops; ++i)5788 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);5789 5790 // Remove unused parts of the input loops.5791 removeUnusedBlocksFromParent(OldControlBBs);5792 5793 for (CanonicalLoopInfo *L : Loops)5794 L->invalidate();5795 5796#ifndef NDEBUG5797 Result->assertOK();5798#endif5799 return Result;5800}5801 5802std::vector<CanonicalLoopInfo *>5803OpenMPIRBuilder::tileLoops(DebugLoc DL, ArrayRef<CanonicalLoopInfo *> Loops,5804 ArrayRef<Value *> TileSizes) {5805 assert(TileSizes.size() == Loops.size() &&5806 "Must pass as many tile sizes as there are loops");5807 int NumLoops = Loops.size();5808 assert(NumLoops >= 1 && "At least one loop to tile required");5809 5810 CanonicalLoopInfo *OutermostLoop = Loops.front();5811 CanonicalLoopInfo *InnermostLoop = Loops.back();5812 Function *F = OutermostLoop->getBody()->getParent();5813 BasicBlock *InnerEnter = InnermostLoop->getBody();5814 BasicBlock *InnerLatch = InnermostLoop->getLatch();5815 5816 // Loop control blocks that may become orphaned later.5817 SmallVector<BasicBlock *, 12> OldControlBBs;5818 OldControlBBs.reserve(6 * Loops.size());5819 for (CanonicalLoopInfo *Loop : Loops)5820 Loop->collectControlBlocks(OldControlBBs);5821 5822 // Collect original trip counts and induction variable to be accessible by5823 // index. Also, the structure of the original loops is not preserved during5824 // the construction of the tiled loops, so do it before we scavenge the BBs of5825 // any original CanonicalLoopInfo.5826 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;5827 for (CanonicalLoopInfo *L : Loops) {5828 assert(L->isValid() && "All input loops must be valid canonical loops");5829 OrigTripCounts.push_back(L->getTripCount());5830 OrigIndVars.push_back(L->getIndVar());5831 }5832 5833 // Collect the code between loop headers. These may contain SSA definitions5834 // that are used in the loop nest body. To be usable with in the innermost5835 // body, these BasicBlocks will be sunk into the loop nest body. That is,5836 // these instructions may be executed more often than before the tiling.5837 // TODO: It would be sufficient to only sink them into body of the5838 // corresponding tile loop.5839 SmallVector<std::pair<BasicBlock *, BasicBlock *>, 4> InbetweenCode;5840 for (int i = 0; i < NumLoops - 1; ++i) {5841 CanonicalLoopInfo *Surrounding = Loops[i];5842 CanonicalLoopInfo *Nested = Loops[i + 1];5843 5844 BasicBlock *EnterBB = Surrounding->getBody();5845 BasicBlock *ExitBB = Nested->getHeader();5846 InbetweenCode.emplace_back(EnterBB, ExitBB);5847 }5848 5849 // Compute the trip counts of the floor loops.5850 Builder.SetCurrentDebugLocation(DL);5851 Builder.restoreIP(OutermostLoop->getPreheaderIP());5852 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;5853 for (int i = 0; i < NumLoops; ++i) {5854 Value *TileSize = TileSizes[i];5855 Value *OrigTripCount = OrigTripCounts[i];5856 Type *IVType = OrigTripCount->getType();5857 5858 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);5859 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);5860 5861 // 0 if tripcount divides the tilesize, 1 otherwise.5862 // 1 means we need an additional iteration for a partial tile.5863 //5864 // Unfortunately we cannot just use the roundup-formula5865 // (tripcount + tilesize - 1)/tilesize5866 // because the summation might overflow. We do not want introduce undefined5867 // behavior when the untiled loop nest did not.5868 Value *FloorTripOverflow =5869 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));5870 5871 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);5872 Value *FloorTripCount =5873 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,5874 "omp_floor" + Twine(i) + ".tripcount", true);5875 5876 // Remember some values for later use.5877 FloorCompleteCount.push_back(FloorCompleteTripCount);5878 FloorCount.push_back(FloorTripCount);5879 FloorRems.push_back(FloorTripRem);5880 }5881 5882 // Generate the new loop nest, from the outermost to the innermost.5883 std::vector<CanonicalLoopInfo *> Result;5884 Result.reserve(NumLoops * 2);5885 5886 // The basic block of the surrounding loop that enters the nest generated5887 // loop.5888 BasicBlock *Enter = OutermostLoop->getPreheader();5889 5890 // The basic block of the surrounding loop where the inner code should5891 // continue.5892 BasicBlock *Continue = OutermostLoop->getAfter();5893 5894 // Where the next loop basic block should be inserted.5895 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();5896 5897 auto EmbeddNewLoop =5898 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](5899 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {5900 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(5901 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);5902 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);5903 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);5904 5905 // Setup the position where the next embedded loop connects to this loop.5906 Enter = EmbeddedLoop->getBody();5907 Continue = EmbeddedLoop->getLatch();5908 OutroInsertBefore = EmbeddedLoop->getLatch();5909 return EmbeddedLoop;5910 };5911 5912 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,5913 const Twine &NameBase) {5914 for (auto P : enumerate(TripCounts)) {5915 CanonicalLoopInfo *EmbeddedLoop =5916 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));5917 Result.push_back(EmbeddedLoop);5918 }5919 };5920 5921 EmbeddNewLoops(FloorCount, "floor");5922 5923 // Within the innermost floor loop, emit the code that computes the tile5924 // sizes.5925 Builder.SetInsertPoint(Enter->getTerminator());5926 SmallVector<Value *, 4> TileCounts;5927 for (int i = 0; i < NumLoops; ++i) {5928 CanonicalLoopInfo *FloorLoop = Result[i];5929 Value *TileSize = TileSizes[i];5930 5931 Value *FloorIsEpilogue =5932 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);5933 Value *TileTripCount =5934 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);5935 5936 TileCounts.push_back(TileTripCount);5937 }5938 5939 // Create the tile loops.5940 EmbeddNewLoops(TileCounts, "tile");5941 5942 // Insert the inbetween code into the body.5943 BasicBlock *BodyEnter = Enter;5944 BasicBlock *BodyEntered = nullptr;5945 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {5946 BasicBlock *EnterBB = P.first;5947 BasicBlock *ExitBB = P.second;5948 5949 if (BodyEnter)5950 redirectTo(BodyEnter, EnterBB, DL);5951 else5952 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);5953 5954 BodyEnter = nullptr;5955 BodyEntered = ExitBB;5956 }5957 5958 // Append the original loop nest body into the generated loop nest body.5959 if (BodyEnter)5960 redirectTo(BodyEnter, InnerEnter, DL);5961 else5962 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);5963 redirectAllPredecessorsTo(InnerLatch, Continue, DL);5964 5965 // Replace the original induction variable with an induction variable computed5966 // from the tile and floor induction variables.5967 Builder.restoreIP(Result.back()->getBodyIP());5968 for (int i = 0; i < NumLoops; ++i) {5969 CanonicalLoopInfo *FloorLoop = Result[i];5970 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];5971 Value *OrigIndVar = OrigIndVars[i];5972 Value *Size = TileSizes[i];5973 5974 Value *Scale =5975 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);5976 Value *Shift =5977 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);5978 OrigIndVar->replaceAllUsesWith(Shift);5979 }5980 5981 // Remove unused parts of the original loops.5982 removeUnusedBlocksFromParent(OldControlBBs);5983 5984 for (CanonicalLoopInfo *L : Loops)5985 L->invalidate();5986 5987#ifndef NDEBUG5988 for (CanonicalLoopInfo *GenL : Result)5989 GenL->assertOK();5990#endif5991 return Result;5992}5993 5994/// Attach metadata \p Properties to the basic block described by \p BB. If the5995/// basic block already has metadata, the basic block properties are appended.5996static void addBasicBlockMetadata(BasicBlock *BB,5997 ArrayRef<Metadata *> Properties) {5998 // Nothing to do if no property to attach.5999 if (Properties.empty())6000 return;6001 6002 LLVMContext &Ctx = BB->getContext();6003 SmallVector<Metadata *> NewProperties;6004 NewProperties.push_back(nullptr);6005 6006 // If the basic block already has metadata, prepend it to the new metadata.6007 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);6008 if (Existing)6009 append_range(NewProperties, drop_begin(Existing->operands(), 1));6010 6011 append_range(NewProperties, Properties);6012 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);6013 BasicBlockID->replaceOperandWith(0, BasicBlockID);6014 6015 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);6016}6017 6018/// Attach loop metadata \p Properties to the loop described by \p Loop. If the6019/// loop already has metadata, the loop properties are appended.6020static void addLoopMetadata(CanonicalLoopInfo *Loop,6021 ArrayRef<Metadata *> Properties) {6022 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");6023 6024 // Attach metadata to the loop's latch6025 BasicBlock *Latch = Loop->getLatch();6026 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");6027 addBasicBlockMetadata(Latch, Properties);6028}6029 6030/// Attach llvm.access.group metadata to the memref instructions of \p Block6031static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,6032 LoopInfo &LI) {6033 for (Instruction &I : *Block) {6034 if (I.mayReadOrWriteMemory()) {6035 // TODO: This instruction may already have access group from6036 // other pragmas e.g. #pragma clang loop vectorize. Append6037 // so that the existing metadata is not overwritten.6038 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);6039 }6040 }6041}6042 6043void OpenMPIRBuilder::unrollLoopFull(DebugLoc, CanonicalLoopInfo *Loop) {6044 LLVMContext &Ctx = Builder.getContext();6045 addLoopMetadata(6046 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),6047 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});6048}6049 6050void OpenMPIRBuilder::unrollLoopHeuristic(DebugLoc, CanonicalLoopInfo *Loop) {6051 LLVMContext &Ctx = Builder.getContext();6052 addLoopMetadata(6053 Loop, {6054 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),6055 });6056}6057 6058void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,6059 Value *IfCond, ValueToValueMapTy &VMap,6060 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,6061 const Twine &NamePrefix) {6062 Function *F = CanonicalLoop->getFunction();6063 6064 // We can't do6065 // if (cond) {6066 // simd_loop;6067 // } else {6068 // non_simd_loop;6069 // }6070 // because then the CanonicalLoopInfo would only point to one of the loops:6071 // leading to other constructs operating on the same loop to malfunction.6072 // Instead generate6073 // while (...) {6074 // if (cond) {6075 // simd_body;6076 // } else {6077 // not_simd_body;6078 // }6079 // }6080 // At least for simple loops, LLVM seems able to hoist the if out of the loop6081 // body at -O36082 6083 // Define where if branch should be inserted6084 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();6085 6086 // Create additional blocks for the if statement6087 BasicBlock *Cond = SplitBeforeIt->getParent();6088 llvm::LLVMContext &C = Cond->getContext();6089 llvm::BasicBlock *ThenBlock = llvm::BasicBlock::Create(6090 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());6091 llvm::BasicBlock *ElseBlock = llvm::BasicBlock::Create(6092 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());6093 6094 // Create if condition branch.6095 Builder.SetInsertPoint(SplitBeforeIt);6096 Instruction *BrInstr =6097 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);6098 InsertPointTy IP{BrInstr->getParent(), ++BrInstr->getIterator()};6099 // Then block contains branch to omp loop body which needs to be vectorized6100 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());6101 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);6102 6103 Builder.SetInsertPoint(ElseBlock);6104 6105 // Clone loop for the else branch6106 SmallVector<BasicBlock *, 8> NewBlocks;6107 6108 SmallVector<BasicBlock *, 8> ExistingBlocks;6109 ExistingBlocks.reserve(L->getNumBlocks() + 1);6110 ExistingBlocks.push_back(ThenBlock);6111 ExistingBlocks.append(L->block_begin(), L->block_end());6112 // Cond is the block that has the if clause condition6113 // LoopCond is omp_loop.cond6114 // LoopHeader is omp_loop.header6115 BasicBlock *LoopCond = Cond->getUniquePredecessor();6116 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();6117 assert(LoopCond && LoopHeader && "Invalid loop structure");6118 for (BasicBlock *Block : ExistingBlocks) {6119 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||6120 Block == LoopHeader || Block == LoopCond || Block == Cond) {6121 continue;6122 }6123 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);6124 6125 // fix name not to be omp.if.then6126 if (Block == ThenBlock)6127 NewBB->setName(NamePrefix + ".if.else");6128 6129 NewBB->moveBefore(CanonicalLoop->getExit());6130 VMap[Block] = NewBB;6131 NewBlocks.push_back(NewBB);6132 }6133 remapInstructionsInBlocks(NewBlocks, VMap);6134 Builder.CreateBr(NewBlocks.front());6135 6136 // The loop latch must have only one predecessor. Currently it is branched to6137 // from both the 'then' and 'else' branches.6138 L->getLoopLatch()->splitBasicBlock(6139 L->getLoopLatch()->begin(), NamePrefix + ".pre_latch", /*Before=*/true);6140 6141 // Ensure that the then block is added to the loop so we add the attributes in6142 // the next step6143 L->addBasicBlockToLoop(ThenBlock, LI);6144}6145 6146unsigned6147OpenMPIRBuilder::getOpenMPDefaultSimdAlign(const Triple &TargetTriple,6148 const StringMap<bool> &Features) {6149 if (TargetTriple.isX86()) {6150 if (Features.lookup("avx512f"))6151 return 512;6152 else if (Features.lookup("avx"))6153 return 256;6154 return 128;6155 }6156 if (TargetTriple.isPPC())6157 return 128;6158 if (TargetTriple.isWasm())6159 return 128;6160 return 0;6161}6162 6163void OpenMPIRBuilder::applySimd(CanonicalLoopInfo *CanonicalLoop,6164 MapVector<Value *, Value *> AlignedVars,6165 Value *IfCond, OrderKind Order,6166 ConstantInt *Simdlen, ConstantInt *Safelen) {6167 LLVMContext &Ctx = Builder.getContext();6168 6169 Function *F = CanonicalLoop->getFunction();6170 6171 // TODO: We should not rely on pass manager. Currently we use pass manager6172 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo6173 // object. We should have a method which returns all blocks between6174 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()6175 FunctionAnalysisManager FAM;6176 FAM.registerPass([]() { return DominatorTreeAnalysis(); });6177 FAM.registerPass([]() { return LoopAnalysis(); });6178 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });6179 6180 LoopAnalysis LIA;6181 LoopInfo &&LI = LIA.run(*F, FAM);6182 6183 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());6184 if (AlignedVars.size()) {6185 InsertPointTy IP = Builder.saveIP();6186 for (auto &AlignedItem : AlignedVars) {6187 Value *AlignedPtr = AlignedItem.first;6188 Value *Alignment = AlignedItem.second;6189 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);6190 Builder.SetInsertPoint(loadInst->getNextNode());6191 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,6192 Alignment);6193 }6194 Builder.restoreIP(IP);6195 }6196 6197 if (IfCond) {6198 ValueToValueMapTy VMap;6199 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");6200 }6201 6202 SmallPtrSet<BasicBlock *, 8> Reachable;6203 6204 // Get the basic blocks from the loop in which memref instructions6205 // can be found.6206 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,6207 // preferably without running any passes.6208 for (BasicBlock *Block : L->getBlocks()) {6209 if (Block == CanonicalLoop->getCond() ||6210 Block == CanonicalLoop->getHeader())6211 continue;6212 Reachable.insert(Block);6213 }6214 6215 SmallVector<Metadata *> LoopMDList;6216 6217 // In presence of finite 'safelen', it may be unsafe to mark all6218 // the memory instructions parallel, because loop-carried6219 // dependences of 'safelen' iterations are possible.6220 // If clause order(concurrent) is specified then the memory instructions6221 // are marked parallel even if 'safelen' is finite.6222 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))6223 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);6224 6225 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD6226 // versions so we can't add the loop attributes in that case.6227 if (IfCond) {6228 // we can still add llvm.loop.parallel_access6229 addLoopMetadata(CanonicalLoop, LoopMDList);6230 return;6231 }6232 6233 // Use the above access group metadata to create loop level6234 // metadata, which should be distinct for each loop.6235 ConstantAsMetadata *BoolConst =6236 ConstantAsMetadata::get(ConstantInt::getTrue(Type::getInt1Ty(Ctx)));6237 LoopMDList.push_back(MDNode::get(6238 Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable"), BoolConst}));6239 6240 if (Simdlen || Safelen) {6241 // If both simdlen and safelen clauses are specified, the value of the6242 // simdlen parameter must be less than or equal to the value of the safelen6243 // parameter. Therefore, use safelen only in the absence of simdlen.6244 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;6245 LoopMDList.push_back(6246 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),6247 ConstantAsMetadata::get(VectorizeWidth)}));6248 }6249 6250 addLoopMetadata(CanonicalLoop, LoopMDList);6251}6252 6253/// Create the TargetMachine object to query the backend for optimization6254/// preferences.6255///6256/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but6257/// e.g. Clang does not pass it to its CodeGen layer and creates it only when6258/// needed for the LLVM pass pipline. We use some default options to avoid6259/// having to pass too many settings from the frontend that probably do not6260/// matter.6261///6262/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial6263/// method. If we are going to use TargetMachine for more purposes, especially6264/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it6265/// might become be worth requiring front-ends to pass on their TargetMachine,6266/// or at least cache it between methods. Note that while fontends such as Clang6267/// have just a single main TargetMachine per translation unit, "target-cpu" and6268/// "target-features" that determine the TargetMachine are per-function and can6269/// be overrided using __attribute__((target("OPTIONS"))).6270static std::unique_ptr<TargetMachine>6271createTargetMachine(Function *F, CodeGenOptLevel OptLevel) {6272 Module *M = F->getParent();6273 6274 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();6275 StringRef Features = F->getFnAttribute("target-features").getValueAsString();6276 const llvm::Triple &Triple = M->getTargetTriple();6277 6278 std::string Error;6279 const llvm::Target *TheTarget = TargetRegistry::lookupTarget(Triple, Error);6280 if (!TheTarget)6281 return {};6282 6283 llvm::TargetOptions Options;6284 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(6285 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,6286 /*CodeModel=*/std::nullopt, OptLevel));6287}6288 6289/// Heuristically determine the best-performant unroll factor for \p CLI. This6290/// depends on the target processor. We are re-using the same heuristics as the6291/// LoopUnrollPass.6292static int32_t computeHeuristicUnrollFactor(CanonicalLoopInfo *CLI) {6293 Function *F = CLI->getFunction();6294 6295 // Assume the user requests the most aggressive unrolling, even if the rest of6296 // the code is optimized using a lower setting.6297 CodeGenOptLevel OptLevel = CodeGenOptLevel::Aggressive;6298 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);6299 6300 FunctionAnalysisManager FAM;6301 FAM.registerPass([]() { return TargetLibraryAnalysis(); });6302 FAM.registerPass([]() { return AssumptionAnalysis(); });6303 FAM.registerPass([]() { return DominatorTreeAnalysis(); });6304 FAM.registerPass([]() { return LoopAnalysis(); });6305 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });6306 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });6307 TargetIRAnalysis TIRA;6308 if (TM)6309 TIRA = TargetIRAnalysis(6310 [&](const Function &F) { return TM->getTargetTransformInfo(F); });6311 FAM.registerPass([&]() { return TIRA; });6312 6313 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);6314 ScalarEvolutionAnalysis SEA;6315 ScalarEvolution &&SE = SEA.run(*F, FAM);6316 DominatorTreeAnalysis DTA;6317 DominatorTree &&DT = DTA.run(*F, FAM);6318 LoopAnalysis LIA;6319 LoopInfo &&LI = LIA.run(*F, FAM);6320 AssumptionAnalysis ACT;6321 AssumptionCache &&AC = ACT.run(*F, FAM);6322 OptimizationRemarkEmitter ORE{F};6323 6324 Loop *L = LI.getLoopFor(CLI->getHeader());6325 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");6326 6327 TargetTransformInfo::UnrollingPreferences UP = gatherUnrollingPreferences(6328 L, SE, TTI,6329 /*BlockFrequencyInfo=*/nullptr,6330 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),6331 /*UserThreshold=*/std::nullopt,6332 /*UserCount=*/std::nullopt,6333 /*UserAllowPartial=*/true,6334 /*UserAllowRuntime=*/true,6335 /*UserUpperBound=*/std::nullopt,6336 /*UserFullUnrollMaxCount=*/std::nullopt);6337 6338 UP.Force = true;6339 6340 // Account for additional optimizations taking place before the LoopUnrollPass6341 // would unroll the loop.6342 UP.Threshold *= UnrollThresholdFactor;6343 UP.PartialThreshold *= UnrollThresholdFactor;6344 6345 // Use normal unroll factors even if the rest of the code is optimized for6346 // size.6347 UP.OptSizeThreshold = UP.Threshold;6348 UP.PartialOptSizeThreshold = UP.PartialThreshold;6349 6350 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"6351 << " Threshold=" << UP.Threshold << "\n"6352 << " PartialThreshold=" << UP.PartialThreshold << "\n"6353 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"6354 << " PartialOptSizeThreshold="6355 << UP.PartialOptSizeThreshold << "\n");6356 6357 // Disable peeling.6358 TargetTransformInfo::PeelingPreferences PP =6359 gatherPeelingPreferences(L, SE, TTI,6360 /*UserAllowPeeling=*/false,6361 /*UserAllowProfileBasedPeeling=*/false,6362 /*UnrollingSpecficValues=*/false);6363 6364 SmallPtrSet<const Value *, 32> EphValues;6365 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);6366 6367 // Assume that reads and writes to stack variables can be eliminated by6368 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's6369 // size.6370 for (BasicBlock *BB : L->blocks()) {6371 for (Instruction &I : *BB) {6372 Value *Ptr;6373 if (auto *Load = dyn_cast<LoadInst>(&I)) {6374 Ptr = Load->getPointerOperand();6375 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {6376 Ptr = Store->getPointerOperand();6377 } else6378 continue;6379 6380 Ptr = Ptr->stripPointerCasts();6381 6382 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {6383 if (Alloca->getParent() == &F->getEntryBlock())6384 EphValues.insert(&I);6385 }6386 }6387 }6388 6389 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);6390 6391 // Loop is not unrollable if the loop contains certain instructions.6392 if (!UCE.canUnroll()) {6393 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");6394 return 1;6395 }6396 6397 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()6398 << "\n");6399 6400 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might6401 // be able to use it.6402 int TripCount = 0;6403 int MaxTripCount = 0;6404 bool MaxOrZero = false;6405 unsigned TripMultiple = 0;6406 6407 bool UseUpperBound = false;6408 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,6409 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP,6410 UseUpperBound);6411 unsigned Factor = UP.Count;6412 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");6413 6414 // This function returns 1 to signal to not unroll a loop.6415 if (Factor == 0)6416 return 1;6417 return Factor;6418}6419 6420void OpenMPIRBuilder::unrollLoopPartial(DebugLoc DL, CanonicalLoopInfo *Loop,6421 int32_t Factor,6422 CanonicalLoopInfo **UnrolledCLI) {6423 assert(Factor >= 0 && "Unroll factor must not be negative");6424 6425 Function *F = Loop->getFunction();6426 LLVMContext &Ctx = F->getContext();6427 6428 // If the unrolled loop is not used for another loop-associated directive, it6429 // is sufficient to add metadata for the LoopUnrollPass.6430 if (!UnrolledCLI) {6431 SmallVector<Metadata *, 2> LoopMetadata;6432 LoopMetadata.push_back(6433 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));6434 6435 if (Factor >= 1) {6436 ConstantAsMetadata *FactorConst = ConstantAsMetadata::get(6437 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));6438 LoopMetadata.push_back(MDNode::get(6439 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));6440 }6441 6442 addLoopMetadata(Loop, LoopMetadata);6443 return;6444 }6445 6446 // Heuristically determine the unroll factor.6447 if (Factor == 0)6448 Factor = computeHeuristicUnrollFactor(Loop);6449 6450 // No change required with unroll factor 1.6451 if (Factor == 1) {6452 *UnrolledCLI = Loop;6453 return;6454 }6455 6456 assert(Factor >= 2 &&6457 "unrolling only makes sense with a factor of 2 or larger");6458 6459 Type *IndVarTy = Loop->getIndVarType();6460 6461 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully6462 // unroll the inner loop.6463 Value *FactorVal =6464 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,6465 /*isSigned=*/false));6466 std::vector<CanonicalLoopInfo *> LoopNest =6467 tileLoops(DL, {Loop}, {FactorVal});6468 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");6469 *UnrolledCLI = LoopNest[0];6470 CanonicalLoopInfo *InnerLoop = LoopNest[1];6471 6472 // LoopUnrollPass can only fully unroll loops with constant trip count.6473 // Unroll by the unroll factor with a fallback epilog for the remainder6474 // iterations if necessary.6475 ConstantAsMetadata *FactorConst = ConstantAsMetadata::get(6476 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));6477 addLoopMetadata(6478 InnerLoop,6479 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),6480 MDNode::get(6481 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});6482 6483#ifndef NDEBUG6484 (*UnrolledCLI)->assertOK();6485#endif6486}6487 6488OpenMPIRBuilder::InsertPointTy6489OpenMPIRBuilder::createCopyPrivate(const LocationDescription &Loc,6490 llvm::Value *BufSize, llvm::Value *CpyBuf,6491 llvm::Value *CpyFn, llvm::Value *DidIt) {6492 if (!updateToLocation(Loc))6493 return Loc.IP;6494 6495 uint32_t SrcLocStrSize;6496 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);6497 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);6498 Value *ThreadId = getOrCreateThreadID(Ident);6499 6500 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);6501 6502 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};6503 6504 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);6505 createRuntimeFunctionCall(Fn, Args);6506 6507 return Builder.saveIP();6508}6509 6510OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createSingle(6511 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,6512 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,6513 ArrayRef<llvm::Function *> CPFuncs) {6514 6515 if (!updateToLocation(Loc))6516 return Loc.IP;6517 6518 // If needed allocate and initialize `DidIt` with 0.6519 // DidIt: flag variable: 1=single thread; 0=not single thread.6520 llvm::Value *DidIt = nullptr;6521 if (!CPVars.empty()) {6522 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));6523 Builder.CreateStore(Builder.getInt32(0), DidIt);6524 }6525 6526 Directive OMPD = Directive::OMPD_single;6527 uint32_t SrcLocStrSize;6528 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);6529 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);6530 Value *ThreadId = getOrCreateThreadID(Ident);6531 Value *Args[] = {Ident, ThreadId};6532 6533 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);6534 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);6535 6536 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);6537 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);6538 6539 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {6540 if (Error Err = FiniCB(IP))6541 return Err;6542 6543 // The thread that executes the single region must set `DidIt` to 1.6544 // This is used by __kmpc_copyprivate, to know if the caller is the6545 // single thread or not.6546 if (DidIt)6547 Builder.CreateStore(Builder.getInt32(1), DidIt);6548 6549 return Error::success();6550 };6551 6552 // generates the following:6553 // if (__kmpc_single()) {6554 // .... single region ...6555 // __kmpc_end_single6556 // }6557 // __kmpc_copyprivate6558 // __kmpc_barrier6559 6560 InsertPointOrErrorTy AfterIP =6561 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,6562 /*Conditional*/ true,6563 /*hasFinalize*/ true);6564 if (!AfterIP)6565 return AfterIP.takeError();6566 6567 if (DidIt) {6568 for (size_t I = 0, E = CPVars.size(); I < E; ++I)6569 // NOTE BufSize is currently unused, so just pass 0.6570 createCopyPrivate(LocationDescription(Builder.saveIP(), Loc.DL),6571 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],6572 CPFuncs[I], DidIt);6573 // NOTE __kmpc_copyprivate already inserts a barrier6574 } else if (!IsNowait) {6575 InsertPointOrErrorTy AfterIP =6576 createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),6577 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,6578 /* CheckCancelFlag */ false);6579 if (!AfterIP)6580 return AfterIP.takeError();6581 }6582 return Builder.saveIP();6583}6584 6585OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createCritical(6586 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,6587 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {6588 6589 if (!updateToLocation(Loc))6590 return Loc.IP;6591 6592 Directive OMPD = Directive::OMPD_critical;6593 uint32_t SrcLocStrSize;6594 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);6595 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);6596 Value *ThreadId = getOrCreateThreadID(Ident);6597 Value *LockVar = getOMPCriticalRegionLock(CriticalName);6598 Value *Args[] = {Ident, ThreadId, LockVar};6599 6600 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));6601 Function *RTFn = nullptr;6602 if (HintInst) {6603 // Add Hint to entry Args and create call6604 EnterArgs.push_back(HintInst);6605 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);6606 } else {6607 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);6608 }6609 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);6610 6611 Function *ExitRTLFn =6612 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);6613 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);6614 6615 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,6616 /*Conditional*/ false, /*hasFinalize*/ true);6617}6618 6619OpenMPIRBuilder::InsertPointTy6620OpenMPIRBuilder::createOrderedDepend(const LocationDescription &Loc,6621 InsertPointTy AllocaIP, unsigned NumLoops,6622 ArrayRef<llvm::Value *> StoreValues,6623 const Twine &Name, bool IsDependSource) {6624 assert(6625 llvm::all_of(StoreValues,6626 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&6627 "OpenMP runtime requires depend vec with i64 type");6628 6629 if (!updateToLocation(Loc))6630 return Loc.IP;6631 6632 // Allocate space for vector and generate alloc instruction.6633 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);6634 Builder.restoreIP(AllocaIP);6635 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);6636 ArgsBase->setAlignment(Align(8));6637 updateToLocation(Loc);6638 6639 // Store the index value with offset in depend vector.6640 for (unsigned I = 0; I < NumLoops; ++I) {6641 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(6642 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});6643 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);6644 STInst->setAlignment(Align(8));6645 }6646 6647 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(6648 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});6649 6650 uint32_t SrcLocStrSize;6651 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);6652 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);6653 Value *ThreadId = getOrCreateThreadID(Ident);6654 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};6655 6656 Function *RTLFn = nullptr;6657 if (IsDependSource)6658 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);6659 else6660 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);6661 createRuntimeFunctionCall(RTLFn, Args);6662 6663 return Builder.saveIP();6664}6665 6666OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createOrderedThreadsSimd(6667 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,6668 FinalizeCallbackTy FiniCB, bool IsThreads) {6669 if (!updateToLocation(Loc))6670 return Loc.IP;6671 6672 Directive OMPD = Directive::OMPD_ordered;6673 Instruction *EntryCall = nullptr;6674 Instruction *ExitCall = nullptr;6675 6676 if (IsThreads) {6677 uint32_t SrcLocStrSize;6678 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);6679 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);6680 Value *ThreadId = getOrCreateThreadID(Ident);6681 Value *Args[] = {Ident, ThreadId};6682 6683 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);6684 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);6685 6686 Function *ExitRTLFn =6687 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);6688 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);6689 }6690 6691 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,6692 /*Conditional*/ false, /*hasFinalize*/ true);6693}6694 6695OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(6696 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,6697 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,6698 bool HasFinalize, bool IsCancellable) {6699 6700 if (HasFinalize)6701 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});6702 6703 // Create inlined region's entry and body blocks, in preparation6704 // for conditional creation6705 BasicBlock *EntryBB = Builder.GetInsertBlock();6706 Instruction *SplitPos = EntryBB->getTerminator();6707 if (!isa_and_nonnull<BranchInst>(SplitPos))6708 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);6709 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");6710 BasicBlock *FiniBB =6711 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");6712 6713 Builder.SetInsertPoint(EntryBB->getTerminator());6714 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);6715 6716 // generate body6717 if (Error Err = BodyGenCB(/* AllocaIP */ InsertPointTy(),6718 /* CodeGenIP */ Builder.saveIP()))6719 return Err;6720 6721 // emit exit call and do any needed finalization.6722 auto FinIP = InsertPointTy(FiniBB, FiniBB->getFirstInsertionPt());6723 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&6724 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&6725 "Unexpected control flow graph state!!");6726 InsertPointOrErrorTy AfterIP =6727 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);6728 if (!AfterIP)6729 return AfterIP.takeError();6730 6731 // If we are skipping the region of a non conditional, remove the exit6732 // block, and clear the builder's insertion point.6733 assert(SplitPos->getParent() == ExitBB &&6734 "Unexpected Insertion point location!");6735 auto merged = MergeBlockIntoPredecessor(ExitBB);6736 BasicBlock *ExitPredBB = SplitPos->getParent();6737 auto InsertBB = merged ? ExitPredBB : ExitBB;6738 if (!isa_and_nonnull<BranchInst>(SplitPos))6739 SplitPos->eraseFromParent();6740 Builder.SetInsertPoint(InsertBB);6741 6742 return Builder.saveIP();6743}6744 6745OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(6746 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {6747 // if nothing to do, Return current insertion point.6748 if (!Conditional || !EntryCall)6749 return Builder.saveIP();6750 6751 BasicBlock *EntryBB = Builder.GetInsertBlock();6752 Value *CallBool = Builder.CreateIsNotNull(EntryCall);6753 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");6754 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);6755 6756 // Emit thenBB and set the Builder's insertion point there for6757 // body generation next. Place the block after the current block.6758 Function *CurFn = EntryBB->getParent();6759 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);6760 6761 // Move Entry branch to end of ThenBB, and replace with conditional6762 // branch (If-stmt)6763 Instruction *EntryBBTI = EntryBB->getTerminator();6764 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);6765 EntryBBTI->removeFromParent();6766 Builder.SetInsertPoint(UI);6767 Builder.Insert(EntryBBTI);6768 UI->eraseFromParent();6769 Builder.SetInsertPoint(ThenBB->getTerminator());6770 6771 // return an insertion point to ExitBB.6772 return IRBuilder<>::InsertPoint(ExitBB, ExitBB->getFirstInsertionPt());6773}6774 6775OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(6776 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,6777 bool HasFinalize) {6778 6779 Builder.restoreIP(FinIP);6780 6781 // If there is finalization to do, emit it before the exit call6782 if (HasFinalize) {6783 assert(!FinalizationStack.empty() &&6784 "Unexpected finalization stack state!");6785 6786 FinalizationInfo Fi = FinalizationStack.pop_back_val();6787 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");6788 6789 if (Error Err = Fi.mergeFiniBB(Builder, FinIP.getBlock()))6790 return std::move(Err);6791 6792 // Exit condition: insertion point is before the terminator of the new Fini6793 // block6794 Builder.SetInsertPoint(FinIP.getBlock()->getTerminator());6795 }6796 6797 if (!ExitCall)6798 return Builder.saveIP();6799 6800 // place the Exitcall as last instruction before Finalization block terminator6801 ExitCall->removeFromParent();6802 Builder.Insert(ExitCall);6803 6804 return IRBuilder<>::InsertPoint(ExitCall->getParent(),6805 ExitCall->getIterator());6806}6807 6808OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createCopyinClauseBlocks(6809 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,6810 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {6811 if (!IP.isSet())6812 return IP;6813 6814 IRBuilder<>::InsertPointGuard IPG(Builder);6815 6816 // creates the following CFG structure6817 // OMP_Entry : (MasterAddr != PrivateAddr)?6818 // F T6819 // | \6820 // | copin.not.master6821 // | /6822 // v /6823 // copyin.not.master.end6824 // |6825 // v6826 // OMP.Entry.Next6827 6828 BasicBlock *OMP_Entry = IP.getBlock();6829 Function *CurFn = OMP_Entry->getParent();6830 BasicBlock *CopyBegin =6831 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);6832 BasicBlock *CopyEnd = nullptr;6833 6834 // If entry block is terminated, split to preserve the branch to following6835 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.6836 if (isa_and_nonnull<BranchInst>(OMP_Entry->getTerminator())) {6837 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),6838 "copyin.not.master.end");6839 OMP_Entry->getTerminator()->eraseFromParent();6840 } else {6841 CopyEnd =6842 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);6843 }6844 6845 Builder.SetInsertPoint(OMP_Entry);6846 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);6847 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);6848 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);6849 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);6850 6851 Builder.SetInsertPoint(CopyBegin);6852 if (BranchtoEnd)6853 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));6854 6855 return Builder.saveIP();6856}6857 6858CallInst *OpenMPIRBuilder::createOMPAlloc(const LocationDescription &Loc,6859 Value *Size, Value *Allocator,6860 std::string Name) {6861 IRBuilder<>::InsertPointGuard IPG(Builder);6862 updateToLocation(Loc);6863 6864 uint32_t SrcLocStrSize;6865 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);6866 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);6867 Value *ThreadId = getOrCreateThreadID(Ident);6868 Value *Args[] = {ThreadId, Size, Allocator};6869 6870 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);6871 6872 return createRuntimeFunctionCall(Fn, Args, Name);6873}6874 6875CallInst *OpenMPIRBuilder::createOMPFree(const LocationDescription &Loc,6876 Value *Addr, Value *Allocator,6877 std::string Name) {6878 IRBuilder<>::InsertPointGuard IPG(Builder);6879 updateToLocation(Loc);6880 6881 uint32_t SrcLocStrSize;6882 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);6883 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);6884 Value *ThreadId = getOrCreateThreadID(Ident);6885 Value *Args[] = {ThreadId, Addr, Allocator};6886 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);6887 return createRuntimeFunctionCall(Fn, Args, Name);6888}6889 6890CallInst *OpenMPIRBuilder::createOMPInteropInit(6891 const LocationDescription &Loc, Value *InteropVar,6892 omp::OMPInteropType InteropType, Value *Device, Value *NumDependences,6893 Value *DependenceAddress, bool HaveNowaitClause) {6894 IRBuilder<>::InsertPointGuard IPG(Builder);6895 updateToLocation(Loc);6896 6897 uint32_t SrcLocStrSize;6898 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);6899 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);6900 Value *ThreadId = getOrCreateThreadID(Ident);6901 if (Device == nullptr)6902 Device = Constant::getAllOnesValue(Int32);6903 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);6904 if (NumDependences == nullptr) {6905 NumDependences = ConstantInt::get(Int32, 0);6906 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());6907 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);6908 }6909 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);6910 Value *Args[] = {6911 Ident, ThreadId, InteropVar, InteropTypeVal,6912 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};6913 6914 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);6915 6916 return createRuntimeFunctionCall(Fn, Args);6917}6918 6919CallInst *OpenMPIRBuilder::createOMPInteropDestroy(6920 const LocationDescription &Loc, Value *InteropVar, Value *Device,6921 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {6922 IRBuilder<>::InsertPointGuard IPG(Builder);6923 updateToLocation(Loc);6924 6925 uint32_t SrcLocStrSize;6926 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);6927 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);6928 Value *ThreadId = getOrCreateThreadID(Ident);6929 if (Device == nullptr)6930 Device = Constant::getAllOnesValue(Int32);6931 if (NumDependences == nullptr) {6932 NumDependences = ConstantInt::get(Int32, 0);6933 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());6934 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);6935 }6936 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);6937 Value *Args[] = {6938 Ident, ThreadId, InteropVar, Device,6939 NumDependences, DependenceAddress, HaveNowaitClauseVal};6940 6941 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);6942 6943 return createRuntimeFunctionCall(Fn, Args);6944}6945 6946CallInst *OpenMPIRBuilder::createOMPInteropUse(const LocationDescription &Loc,6947 Value *InteropVar, Value *Device,6948 Value *NumDependences,6949 Value *DependenceAddress,6950 bool HaveNowaitClause) {6951 IRBuilder<>::InsertPointGuard IPG(Builder);6952 updateToLocation(Loc);6953 uint32_t SrcLocStrSize;6954 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);6955 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);6956 Value *ThreadId = getOrCreateThreadID(Ident);6957 if (Device == nullptr)6958 Device = Constant::getAllOnesValue(Int32);6959 if (NumDependences == nullptr) {6960 NumDependences = ConstantInt::get(Int32, 0);6961 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());6962 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);6963 }6964 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);6965 Value *Args[] = {6966 Ident, ThreadId, InteropVar, Device,6967 NumDependences, DependenceAddress, HaveNowaitClauseVal};6968 6969 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);6970 6971 return createRuntimeFunctionCall(Fn, Args);6972}6973 6974CallInst *OpenMPIRBuilder::createCachedThreadPrivate(6975 const LocationDescription &Loc, llvm::Value *Pointer,6976 llvm::ConstantInt *Size, const llvm::Twine &Name) {6977 IRBuilder<>::InsertPointGuard IPG(Builder);6978 updateToLocation(Loc);6979 6980 uint32_t SrcLocStrSize;6981 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);6982 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);6983 Value *ThreadId = getOrCreateThreadID(Ident);6984 Constant *ThreadPrivateCache =6985 getOrCreateInternalVariable(Int8PtrPtr, Name.str());6986 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};6987 6988 Function *Fn =6989 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);6990 6991 return createRuntimeFunctionCall(Fn, Args);6992}6993 6994OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInit(6995 const LocationDescription &Loc,6996 const llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs &Attrs) {6997 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&6998 "expected num_threads and num_teams to be specified");6999 7000 if (!updateToLocation(Loc))7001 return Loc.IP;7002 7003 uint32_t SrcLocStrSize;7004 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);7005 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);7006 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);7007 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(7008 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD);7009 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);7010 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);7011 7012 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();7013 Function *Kernel = DebugKernelWrapper;7014 7015 // We need to strip the debug prefix to get the correct kernel name.7016 StringRef KernelName = Kernel->getName();7017 const std::string DebugPrefix = "_debug__";7018 if (KernelName.ends_with(DebugPrefix)) {7019 KernelName = KernelName.drop_back(DebugPrefix.length());7020 Kernel = M.getFunction(KernelName);7021 assert(Kernel && "Expected the real kernel to exist");7022 }7023 7024 // Manifest the launch configuration in the metadata matching the kernel7025 // environment.7026 if (Attrs.MinTeams > 1 || Attrs.MaxTeams.front() > 0)7027 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams, Attrs.MaxTeams.front());7028 7029 // If MaxThreads not set, select the maximum between the default workgroup7030 // size and the MinThreads value.7031 int32_t MaxThreadsVal = Attrs.MaxThreads.front();7032 if (MaxThreadsVal < 0)7033 MaxThreadsVal = std::max(7034 int32_t(getGridValue(T, Kernel).GV_Default_WG_Size), Attrs.MinThreads);7035 7036 if (MaxThreadsVal > 0)7037 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads, MaxThreadsVal);7038 7039 Constant *MinThreads = ConstantInt::getSigned(Int32, Attrs.MinThreads);7040 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);7041 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams);7042 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());7043 Constant *ReductionDataSize =7044 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);7045 Constant *ReductionBufferLength =7046 ConstantInt::getSigned(Int32, Attrs.ReductionBufferLength);7047 7048 Function *Fn = getOrCreateRuntimeFunctionPtr(7049 omp::RuntimeFunction::OMPRTL___kmpc_target_init);7050 const DataLayout &DL = Fn->getDataLayout();7051 7052 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";7053 Constant *DynamicEnvironmentInitializer =7054 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});7055 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(7056 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,7057 DynamicEnvironmentInitializer, DynamicEnvironmentName,7058 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,7059 DL.getDefaultGlobalsAddressSpace());7060 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);7061 7062 Constant *DynamicEnvironment =7063 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr7064 ? DynamicEnvironmentGV7065 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,7066 DynamicEnvironmentPtr);7067 7068 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(7069 ConfigurationEnvironment, {7070 UseGenericStateMachineVal,7071 MayUseNestedParallelismVal,7072 IsSPMDVal,7073 MinThreads,7074 MaxThreads,7075 MinTeams,7076 MaxTeams,7077 ReductionDataSize,7078 ReductionBufferLength,7079 });7080 Constant *KernelEnvironmentInitializer = ConstantStruct::get(7081 KernelEnvironment, {7082 ConfigurationEnvironmentInitializer,7083 Ident,7084 DynamicEnvironment,7085 });7086 std::string KernelEnvironmentName =7087 (KernelName + "_kernel_environment").str();7088 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(7089 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,7090 KernelEnvironmentInitializer, KernelEnvironmentName,7091 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,7092 DL.getDefaultGlobalsAddressSpace());7093 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);7094 7095 Constant *KernelEnvironment =7096 KernelEnvironmentGV->getType() == KernelEnvironmentPtr7097 ? KernelEnvironmentGV7098 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,7099 KernelEnvironmentPtr);7100 Value *KernelLaunchEnvironment = DebugKernelWrapper->getArg(0);7101 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);7102 KernelLaunchEnvironment =7103 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy7104 ? KernelLaunchEnvironment7105 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,7106 KernelLaunchEnvParamTy);7107 CallInst *ThreadKind = createRuntimeFunctionCall(7108 Fn, {KernelEnvironment, KernelLaunchEnvironment});7109 7110 Value *ExecUserCode = Builder.CreateICmpEQ(7111 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),7112 "exec_user_code");7113 7114 // ThreadKind = __kmpc_target_init(...)7115 // if (ThreadKind == -1)7116 // user_code7117 // else7118 // return;7119 7120 auto *UI = Builder.CreateUnreachable();7121 BasicBlock *CheckBB = UI->getParent();7122 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");7123 7124 BasicBlock *WorkerExitBB = BasicBlock::Create(7125 CheckBB->getContext(), "worker.exit", CheckBB->getParent());7126 Builder.SetInsertPoint(WorkerExitBB);7127 Builder.CreateRetVoid();7128 7129 auto *CheckBBTI = CheckBB->getTerminator();7130 Builder.SetInsertPoint(CheckBBTI);7131 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);7132 7133 CheckBBTI->eraseFromParent();7134 UI->eraseFromParent();7135 7136 // Continue in the "user_code" block, see diagram above and in7137 // openmp/libomptarget/deviceRTLs/common/include/target.h .7138 return InsertPointTy(UserCodeEntryBB, UserCodeEntryBB->getFirstInsertionPt());7139}7140 7141void OpenMPIRBuilder::createTargetDeinit(const LocationDescription &Loc,7142 int32_t TeamsReductionDataSize,7143 int32_t TeamsReductionBufferLength) {7144 if (!updateToLocation(Loc))7145 return;7146 7147 Function *Fn = getOrCreateRuntimeFunctionPtr(7148 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);7149 7150 createRuntimeFunctionCall(Fn, {});7151 7152 if (!TeamsReductionBufferLength || !TeamsReductionDataSize)7153 return;7154 7155 Function *Kernel = Builder.GetInsertBlock()->getParent();7156 // We need to strip the debug prefix to get the correct kernel name.7157 StringRef KernelName = Kernel->getName();7158 const std::string DebugPrefix = "_debug__";7159 if (KernelName.ends_with(DebugPrefix))7160 KernelName = KernelName.drop_back(DebugPrefix.length());7161 auto *KernelEnvironmentGV =7162 M.getNamedGlobal((KernelName + "_kernel_environment").str());7163 assert(KernelEnvironmentGV && "Expected kernel environment global\n");7164 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();7165 auto *NewInitializer = ConstantFoldInsertValueInstruction(7166 KernelEnvironmentInitializer,7167 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});7168 NewInitializer = ConstantFoldInsertValueInstruction(7169 NewInitializer, ConstantInt::get(Int32, TeamsReductionBufferLength),7170 {0, 8});7171 KernelEnvironmentGV->setInitializer(NewInitializer);7172}7173 7174static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,7175 bool Min) {7176 if (Kernel.hasFnAttribute(Name)) {7177 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);7178 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);7179 }7180 Kernel.addFnAttr(Name, llvm::utostr(Value));7181}7182 7183std::pair<int32_t, int32_t>7184OpenMPIRBuilder::readThreadBoundsForKernel(const Triple &T, Function &Kernel) {7185 int32_t ThreadLimit =7186 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");7187 7188 if (T.isAMDGPU()) {7189 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");7190 if (!Attr.isValid() || !Attr.isStringAttribute())7191 return {0, ThreadLimit};7192 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');7193 int32_t LB, UB;7194 if (!llvm::to_integer(UBStr, UB, 10))7195 return {0, ThreadLimit};7196 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;7197 if (!llvm::to_integer(LBStr, LB, 10))7198 return {0, UB};7199 return {LB, UB};7200 }7201 7202 if (Kernel.hasFnAttribute("nvvm.maxntid")) {7203 int32_t UB = Kernel.getFnAttributeAsParsedInteger("nvvm.maxntid");7204 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};7205 }7206 return {0, ThreadLimit};7207}7208 7209void OpenMPIRBuilder::writeThreadBoundsForKernel(const Triple &T,7210 Function &Kernel, int32_t LB,7211 int32_t UB) {7212 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));7213 7214 if (T.isAMDGPU()) {7215 Kernel.addFnAttr("amdgpu-flat-work-group-size",7216 llvm::utostr(LB) + "," + llvm::utostr(UB));7217 return;7218 }7219 7220 updateNVPTXAttr(Kernel, "nvvm.maxntid", UB, true);7221}7222 7223std::pair<int32_t, int32_t>7224OpenMPIRBuilder::readTeamBoundsForKernel(const Triple &, Function &Kernel) {7225 // TODO: Read from backend annotations if available.7226 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};7227}7228 7229void OpenMPIRBuilder::writeTeamsForKernel(const Triple &T, Function &Kernel,7230 int32_t LB, int32_t UB) {7231 if (T.isNVPTX())7232 if (UB > 0)7233 Kernel.addFnAttr("nvvm.maxclusterrank", llvm::utostr(UB));7234 if (T.isAMDGPU())7235 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(LB) + ",1,1");7236 7237 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));7238}7239 7240void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(7241 Function *OutlinedFn) {7242 if (Config.isTargetDevice()) {7243 OutlinedFn->setLinkage(GlobalValue::WeakODRLinkage);7244 // TODO: Determine if DSO local can be set to true.7245 OutlinedFn->setDSOLocal(false);7246 OutlinedFn->setVisibility(GlobalValue::ProtectedVisibility);7247 if (T.isAMDGCN())7248 OutlinedFn->setCallingConv(CallingConv::AMDGPU_KERNEL);7249 else if (T.isNVPTX())7250 OutlinedFn->setCallingConv(CallingConv::PTX_Kernel);7251 else if (T.isSPIRV())7252 OutlinedFn->setCallingConv(CallingConv::SPIR_KERNEL);7253 }7254}7255 7256Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,7257 StringRef EntryFnIDName) {7258 if (Config.isTargetDevice()) {7259 assert(OutlinedFn && "The outlined function must exist if embedded");7260 return OutlinedFn;7261 }7262 7263 return new GlobalVariable(7264 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,7265 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);7266}7267 7268Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,7269 StringRef EntryFnName) {7270 if (OutlinedFn)7271 return OutlinedFn;7272 7273 assert(!M.getGlobalVariable(EntryFnName, true) &&7274 "Named kernel already exists?");7275 return new GlobalVariable(7276 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,7277 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);7278}7279 7280Error OpenMPIRBuilder::emitTargetRegionFunction(7281 TargetRegionEntryInfo &EntryInfo,7282 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,7283 Function *&OutlinedFn, Constant *&OutlinedFnID) {7284 7285 SmallString<64> EntryFnName;7286 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);7287 7288 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {7289 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);7290 if (!CBResult)7291 return CBResult.takeError();7292 OutlinedFn = *CBResult;7293 } else {7294 OutlinedFn = nullptr;7295 }7296 7297 // If this target outline function is not an offload entry, we don't need to7298 // register it. This may be in the case of a false if clause, or if there are7299 // no OpenMP targets.7300 if (!IsOffloadEntry)7301 return Error::success();7302 7303 std::string EntryFnIDName =7304 Config.isTargetDevice()7305 ? std::string(EntryFnName)7306 : createPlatformSpecificName({EntryFnName, "region_id"});7307 7308 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,7309 EntryFnName, EntryFnIDName);7310 return Error::success();7311}7312 7313Constant *OpenMPIRBuilder::registerTargetRegionFunction(7314 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,7315 StringRef EntryFnName, StringRef EntryFnIDName) {7316 if (OutlinedFn)7317 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);7318 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);7319 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);7320 OffloadInfoManager.registerTargetRegionEntryInfo(7321 EntryInfo, EntryAddr, OutlinedFnID,7322 OffloadEntriesInfoManager::OMPTargetRegionEntryTargetRegion);7323 return OutlinedFnID;7324}7325 7326OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTargetData(7327 const LocationDescription &Loc, InsertPointTy AllocaIP,7328 InsertPointTy CodeGenIP, Value *DeviceID, Value *IfCond,7329 TargetDataInfo &Info, GenMapInfoCallbackTy GenMapInfoCB,7330 CustomMapperCallbackTy CustomMapperCB, omp::RuntimeFunction *MapperFunc,7331 function_ref<InsertPointOrErrorTy(InsertPointTy CodeGenIP,7332 BodyGenTy BodyGenType)>7333 BodyGenCB,7334 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {7335 if (!updateToLocation(Loc))7336 return InsertPointTy();7337 7338 Builder.restoreIP(CodeGenIP);7339 // Disable TargetData CodeGen on Device pass.7340 if (Config.IsTargetDevice.value_or(false)) {7341 if (BodyGenCB) {7342 InsertPointOrErrorTy AfterIP =7343 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);7344 if (!AfterIP)7345 return AfterIP.takeError();7346 Builder.restoreIP(*AfterIP);7347 }7348 return Builder.saveIP();7349 }7350 7351 bool IsStandAlone = !BodyGenCB;7352 MapInfosTy *MapInfo;7353 // Generate the code for the opening of the data environment. Capture all the7354 // arguments of the runtime call by reference because they are used in the7355 // closing of the region.7356 auto BeginThenGen = [&](InsertPointTy AllocaIP,7357 InsertPointTy CodeGenIP) -> Error {7358 MapInfo = &GenMapInfoCB(Builder.saveIP());7359 if (Error Err = emitOffloadingArrays(7360 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,7361 /*IsNonContiguous=*/true, DeviceAddrCB))7362 return Err;7363 7364 TargetDataRTArgs RTArgs;7365 emitOffloadingArraysArgument(Builder, RTArgs, Info);7366 7367 // Emit the number of elements in the offloading arrays.7368 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);7369 7370 // Source location for the ident struct7371 if (!SrcLocInfo) {7372 uint32_t SrcLocStrSize;7373 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);7374 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);7375 }7376 7377 SmallVector<llvm::Value *, 13> OffloadingArgs = {7378 SrcLocInfo, DeviceID,7379 PointerNum, RTArgs.BasePointersArray,7380 RTArgs.PointersArray, RTArgs.SizesArray,7381 RTArgs.MapTypesArray, RTArgs.MapNamesArray,7382 RTArgs.MappersArray};7383 7384 if (IsStandAlone) {7385 assert(MapperFunc && "MapperFunc missing for standalone target data");7386 7387 auto TaskBodyCB = [&](Value *, Value *,7388 IRBuilderBase::InsertPoint) -> Error {7389 if (Info.HasNoWait) {7390 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),7391 llvm::Constant::getNullValue(VoidPtr),7392 llvm::Constant::getNullValue(Int32),7393 llvm::Constant::getNullValue(VoidPtr)});7394 }7395 7396 createRuntimeFunctionCall(getOrCreateRuntimeFunctionPtr(*MapperFunc),7397 OffloadingArgs);7398 7399 if (Info.HasNoWait) {7400 BasicBlock *OffloadContBlock =7401 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");7402 Function *CurFn = Builder.GetInsertBlock()->getParent();7403 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);7404 Builder.restoreIP(Builder.saveIP());7405 }7406 return Error::success();7407 };7408 7409 bool RequiresOuterTargetTask = Info.HasNoWait;7410 if (!RequiresOuterTargetTask)7411 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,7412 /*TargetTaskAllocaIP=*/{}));7413 else7414 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,7415 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));7416 } else {7417 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(7418 omp::OMPRTL___tgt_target_data_begin_mapper);7419 7420 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);7421 7422 for (auto DeviceMap : Info.DevicePtrInfoMap) {7423 if (isa<AllocaInst>(DeviceMap.second.second)) {7424 auto *LI =7425 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);7426 Builder.CreateStore(LI, DeviceMap.second.second);7427 }7428 }7429 7430 // If device pointer privatization is required, emit the body of the7431 // region here. It will have to be duplicated: with and without7432 // privatization.7433 InsertPointOrErrorTy AfterIP =7434 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);7435 if (!AfterIP)7436 return AfterIP.takeError();7437 Builder.restoreIP(*AfterIP);7438 }7439 return Error::success();7440 };7441 7442 // If we need device pointer privatization, we need to emit the body of the7443 // region with no privatization in the 'else' branch of the conditional.7444 // Otherwise, we don't have to do anything.7445 auto BeginElseGen = [&](InsertPointTy AllocaIP,7446 InsertPointTy CodeGenIP) -> Error {7447 InsertPointOrErrorTy AfterIP =7448 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);7449 if (!AfterIP)7450 return AfterIP.takeError();7451 Builder.restoreIP(*AfterIP);7452 return Error::success();7453 };7454 7455 // Generate code for the closing of the data region.7456 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {7457 TargetDataRTArgs RTArgs;7458 Info.EmitDebug = !MapInfo->Names.empty();7459 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);7460 7461 // Emit the number of elements in the offloading arrays.7462 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);7463 7464 // Source location for the ident struct7465 if (!SrcLocInfo) {7466 uint32_t SrcLocStrSize;7467 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);7468 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);7469 }7470 7471 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,7472 PointerNum, RTArgs.BasePointersArray,7473 RTArgs.PointersArray, RTArgs.SizesArray,7474 RTArgs.MapTypesArray, RTArgs.MapNamesArray,7475 RTArgs.MappersArray};7476 Function *EndMapperFunc =7477 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);7478 7479 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);7480 return Error::success();7481 };7482 7483 // We don't have to do anything to close the region if the if clause evaluates7484 // to false.7485 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {7486 return Error::success();7487 };7488 7489 Error Err = [&]() -> Error {7490 if (BodyGenCB) {7491 Error Err = [&]() {7492 if (IfCond)7493 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);7494 return BeginThenGen(AllocaIP, Builder.saveIP());7495 }();7496 7497 if (Err)7498 return Err;7499 7500 // If we don't require privatization of device pointers, we emit the body7501 // in between the runtime calls. This avoids duplicating the body code.7502 InsertPointOrErrorTy AfterIP =7503 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);7504 if (!AfterIP)7505 return AfterIP.takeError();7506 restoreIPandDebugLoc(Builder, *AfterIP);7507 7508 if (IfCond)7509 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);7510 return EndThenGen(AllocaIP, Builder.saveIP());7511 }7512 if (IfCond)7513 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);7514 return BeginThenGen(AllocaIP, Builder.saveIP());7515 }();7516 7517 if (Err)7518 return Err;7519 7520 return Builder.saveIP();7521}7522 7523FunctionCallee7524OpenMPIRBuilder::createForStaticInitFunction(unsigned IVSize, bool IVSigned,7525 bool IsGPUDistribute) {7526 assert((IVSize == 32 || IVSize == 64) &&7527 "IV size is not compatible with the omp runtime");7528 RuntimeFunction Name;7529 if (IsGPUDistribute)7530 Name = IVSize == 327531 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_47532 : omp::OMPRTL___kmpc_distribute_static_init_4u)7533 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_87534 : omp::OMPRTL___kmpc_distribute_static_init_8u);7535 else7536 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_47537 : omp::OMPRTL___kmpc_for_static_init_4u)7538 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_87539 : omp::OMPRTL___kmpc_for_static_init_8u);7540 7541 return getOrCreateRuntimeFunction(M, Name);7542}7543 7544FunctionCallee OpenMPIRBuilder::createDispatchInitFunction(unsigned IVSize,7545 bool IVSigned) {7546 assert((IVSize == 32 || IVSize == 64) &&7547 "IV size is not compatible with the omp runtime");7548 RuntimeFunction Name = IVSize == 327549 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_47550 : omp::OMPRTL___kmpc_dispatch_init_4u)7551 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_87552 : omp::OMPRTL___kmpc_dispatch_init_8u);7553 7554 return getOrCreateRuntimeFunction(M, Name);7555}7556 7557FunctionCallee OpenMPIRBuilder::createDispatchNextFunction(unsigned IVSize,7558 bool IVSigned) {7559 assert((IVSize == 32 || IVSize == 64) &&7560 "IV size is not compatible with the omp runtime");7561 RuntimeFunction Name = IVSize == 327562 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_47563 : omp::OMPRTL___kmpc_dispatch_next_4u)7564 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_87565 : omp::OMPRTL___kmpc_dispatch_next_8u);7566 7567 return getOrCreateRuntimeFunction(M, Name);7568}7569 7570FunctionCallee OpenMPIRBuilder::createDispatchFiniFunction(unsigned IVSize,7571 bool IVSigned) {7572 assert((IVSize == 32 || IVSize == 64) &&7573 "IV size is not compatible with the omp runtime");7574 RuntimeFunction Name = IVSize == 327575 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_47576 : omp::OMPRTL___kmpc_dispatch_fini_4u)7577 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_87578 : omp::OMPRTL___kmpc_dispatch_fini_8u);7579 7580 return getOrCreateRuntimeFunction(M, Name);7581}7582 7583FunctionCallee OpenMPIRBuilder::createDispatchDeinitFunction() {7584 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);7585}7586 7587static void FixupDebugInfoForOutlinedFunction(7588 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,7589 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {7590 7591 DISubprogram *NewSP = Func->getSubprogram();7592 if (!NewSP)7593 return;7594 7595 SmallDenseMap<DILocalVariable *, DILocalVariable *> RemappedVariables;7596 7597 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {7598 DILocalVariable *&NewVar = RemappedVariables[OldVar];7599 // Only use cached variable if the arg number matches. This is important7600 // so that DIVariable created for privatized variables are not discarded.7601 if (NewVar && (arg == NewVar->getArg()))7602 return NewVar;7603 7604 NewVar = llvm::DILocalVariable::get(7605 Builder.getContext(), OldVar->getScope(), OldVar->getName(),7606 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,7607 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());7608 return NewVar;7609 };7610 7611 auto UpdateDebugRecord = [&](auto *DR) {7612 DILocalVariable *OldVar = DR->getVariable();7613 unsigned ArgNo = 0;7614 for (auto Loc : DR->location_ops()) {7615 auto Iter = ValueReplacementMap.find(Loc);7616 if (Iter != ValueReplacementMap.end()) {7617 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));7618 ArgNo = std::get<1>(Iter->second) + 1;7619 }7620 }7621 if (ArgNo != 0)7622 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));7623 };7624 7625 // The location and scope of variable intrinsics and records still point to7626 // the parent function of the target region. Update them.7627 for (Instruction &I : instructions(Func)) {7628 assert(!isa<llvm::DbgVariableIntrinsic>(&I) &&7629 "Unexpected debug intrinsic");7630 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange()))7631 UpdateDebugRecord(&DVR);7632 }7633 // An extra argument is passed to the device. Create the debug data for it.7634 if (OMPBuilder.Config.isTargetDevice()) {7635 DICompileUnit *CU = NewSP->getUnit();7636 Module *M = Func->getParent();7637 DIBuilder DB(*M, true, CU);7638 DIType *VoidPtrTy =7639 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);7640 DILocalVariable *Var = DB.createParameterVariable(7641 NewSP, "dyn_ptr", /*ArgNo*/ 1, NewSP->getFile(), /*LineNo=*/0,7642 VoidPtrTy, /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);7643 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);7644 DB.insertDeclare(&(*Func->arg_begin()), Var, DB.createExpression(), Loc,7645 &(*Func->begin()));7646 }7647}7648 7649static Value *removeASCastIfPresent(Value *V) {7650 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)7651 return cast<Operator>(V)->getOperand(0);7652 return V;7653}7654 7655static Expected<Function *> createOutlinedFunction(7656 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,7657 const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs,7658 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,7659 OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc,7660 OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB) {7661 SmallVector<Type *> ParameterTypes;7662 if (OMPBuilder.Config.isTargetDevice()) {7663 // Add the "implicit" runtime argument we use to provide launch specific7664 // information for target devices.7665 auto *Int8PtrTy = PointerType::getUnqual(Builder.getContext());7666 ParameterTypes.push_back(Int8PtrTy);7667 7668 // All parameters to target devices are passed as pointers7669 // or i64. This assumes 64-bit address spaces/pointers.7670 for (auto &Arg : Inputs)7671 ParameterTypes.push_back(Arg->getType()->isPointerTy()7672 ? Arg->getType()7673 : Type::getInt64Ty(Builder.getContext()));7674 } else {7675 for (auto &Arg : Inputs)7676 ParameterTypes.push_back(Arg->getType());7677 }7678 7679 auto BB = Builder.GetInsertBlock();7680 auto M = BB->getModule();7681 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,7682 /*isVarArg*/ false);7683 auto Func =7684 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);7685 7686 // Forward target-cpu and target-features function attributes from the7687 // original function to the new outlined function.7688 Function *ParentFn = Builder.GetInsertBlock()->getParent();7689 7690 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");7691 if (TargetCpuAttr.isStringAttribute())7692 Func->addFnAttr(TargetCpuAttr);7693 7694 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");7695 if (TargetFeaturesAttr.isStringAttribute())7696 Func->addFnAttr(TargetFeaturesAttr);7697 7698 if (OMPBuilder.Config.isTargetDevice()) {7699 Value *ExecMode =7700 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);7701 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});7702 }7703 7704 // Save insert point.7705 IRBuilder<>::InsertPointGuard IPG(Builder);7706 // We will generate the entries in the outlined function but the debug7707 // location may still be pointing to the parent function. Reset it now.7708 Builder.SetCurrentDebugLocation(llvm::DebugLoc());7709 7710 // Generate the region into the function.7711 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);7712 Builder.SetInsertPoint(EntryBB);7713 7714 // Insert target init call in the device compilation pass.7715 if (OMPBuilder.Config.isTargetDevice())7716 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));7717 7718 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();7719 7720 // As we embed the user code in the middle of our target region after we7721 // generate entry code, we must move what allocas we can into the entry7722 // block to avoid possible breaking optimisations for device7723 if (OMPBuilder.Config.isTargetDevice())7724 OMPBuilder.ConstantAllocaRaiseCandidates.emplace_back(Func);7725 7726 // Insert target deinit call in the device compilation pass.7727 BasicBlock *OutlinedBodyBB =7728 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");7729 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = CBFunc(7730 Builder.saveIP(),7731 OpenMPIRBuilder::InsertPointTy(OutlinedBodyBB, OutlinedBodyBB->begin()));7732 if (!AfterIP)7733 return AfterIP.takeError();7734 Builder.restoreIP(*AfterIP);7735 if (OMPBuilder.Config.isTargetDevice())7736 OMPBuilder.createTargetDeinit(Builder);7737 7738 // Insert return instruction.7739 Builder.CreateRetVoid();7740 7741 // New Alloca IP at entry point of created device function.7742 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());7743 auto AllocaIP = Builder.saveIP();7744 7745 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());7746 7747 // Skip the artificial dyn_ptr on the device.7748 const auto &ArgRange =7749 OMPBuilder.Config.isTargetDevice()7750 ? make_range(Func->arg_begin() + 1, Func->arg_end())7751 : Func->args();7752 7753 DenseMap<Value *, std::tuple<Value *, unsigned>> ValueReplacementMap;7754 7755 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {7756 // Things like GEP's can come in the form of Constants. Constants and7757 // ConstantExpr's do not have access to the knowledge of what they're7758 // contained in, so we must dig a little to find an instruction so we7759 // can tell if they're used inside of the function we're outlining. We7760 // also replace the original constant expression with a new instruction7761 // equivalent; an instruction as it allows easy modification in the7762 // following loop, as we can now know the constant (instruction) is7763 // owned by our target function and replaceUsesOfWith can now be invoked7764 // on it (cannot do this with constants it seems). A brand new one also7765 // allows us to be cautious as it is perhaps possible the old expression7766 // was used inside of the function but exists and is used externally7767 // (unlikely by the nature of a Constant, but still).7768 // NOTE: We cannot remove dead constants that have been rewritten to7769 // instructions at this stage, we run the risk of breaking later lowering7770 // by doing so as we could still be in the process of lowering the module7771 // from MLIR to LLVM-IR and the MLIR lowering may still require the original7772 // constants we have created rewritten versions of.7773 if (auto *Const = dyn_cast<Constant>(Input))7774 convertUsersOfConstantsToInstructions(Const, Func, false);7775 7776 // Collect users before iterating over them to avoid invalidating the7777 // iteration in case a user uses Input more than once (e.g. a call7778 // instruction).7779 SetVector<User *> Users(Input->users().begin(), Input->users().end());7780 // Collect all the instructions7781 for (User *User : make_early_inc_range(Users))7782 if (auto *Instr = dyn_cast<Instruction>(User))7783 if (Instr->getFunction() == Func)7784 Instr->replaceUsesOfWith(Input, InputCopy);7785 };7786 7787 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;7788 7789 // Rewrite uses of input valus to parameters.7790 for (auto InArg : zip(Inputs, ArgRange)) {7791 Value *Input = std::get<0>(InArg);7792 Argument &Arg = std::get<1>(InArg);7793 Value *InputCopy = nullptr;7794 7795 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP =7796 ArgAccessorFuncCB(Arg, Input, InputCopy, AllocaIP, Builder.saveIP());7797 if (!AfterIP)7798 return AfterIP.takeError();7799 Builder.restoreIP(*AfterIP);7800 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());7801 7802 // In certain cases a Global may be set up for replacement, however, this7803 // Global may be used in multiple arguments to the kernel, just segmented7804 // apart, for example, if we have a global array, that is sectioned into7805 // multiple mappings (technically not legal in OpenMP, but there is a case7806 // in Fortran for Common Blocks where this is neccesary), we will end up7807 // with GEP's into this array inside the kernel, that refer to the Global7808 // but are technically seperate arguments to the kernel for all intents and7809 // purposes. If we have mapped a segment that requires a GEP into the 0-th7810 // index, it will fold into an referal to the Global, if we then encounter7811 // this folded GEP during replacement all of the references to the7812 // Global in the kernel will be replaced with the argument we have generated7813 // that corresponds to it, including any other GEP's that refer to the7814 // Global that may be other arguments. This will invalidate all of the other7815 // preceding mapped arguments that refer to the same global that may be7816 // seperate segments. To prevent this, we defer global processing until all7817 // other processing has been performed.7818 if (llvm::isa<llvm::GlobalValue, llvm::GlobalObject, llvm::GlobalVariable>(7819 removeASCastIfPresent(Input))) {7820 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));7821 continue;7822 }7823 7824 if (isa<ConstantData>(Input))7825 continue;7826 7827 ReplaceValue(Input, InputCopy, Func);7828 }7829 7830 // Replace all of our deferred Input values, currently just Globals.7831 for (auto Deferred : DeferredReplacement)7832 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);7833 7834 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,7835 ValueReplacementMap);7836 return Func;7837}7838/// Given a task descriptor, TaskWithPrivates, return the pointer to the block7839/// of pointers containing shared data between the parent task and the created7840/// task.7841static LoadInst *loadSharedDataFromTaskDescriptor(OpenMPIRBuilder &OMPIRBuilder,7842 IRBuilderBase &Builder,7843 Value *TaskWithPrivates,7844 Type *TaskWithPrivatesTy) {7845 7846 Type *TaskTy = OMPIRBuilder.Task;7847 LLVMContext &Ctx = Builder.getContext();7848 Value *TaskT =7849 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);7850 Value *Shareds = TaskT;7851 // TaskWithPrivatesTy can be one of the following7852 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,7853 // %struct.privates }7854 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy7855 //7856 // In the former case, that is when TaskWithPrivatesTy != TaskTy,7857 // its first member has to be the task descriptor. TaskTy is the type of the7858 // task descriptor. TaskT is the pointer to the task descriptor. Loading the7859 // first member of TaskT, gives us the pointer to shared data.7860 if (TaskWithPrivatesTy != TaskTy)7861 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);7862 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);7863}7864/// Create an entry point for a target task with the following.7865/// It'll have the following signature7866/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)7867/// This function is called from emitTargetTask once the7868/// code to launch the target kernel has been outlined already.7869/// NumOffloadingArrays is the number of offloading arrays that we need to copy7870/// into the task structure so that the deferred target task can access this7871/// data even after the stack frame of the generating task has been rolled7872/// back. Offloading arrays contain base pointers, pointers, sizes etc7873/// of the data that the target kernel will access. These in effect are the7874/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.7875static Function *emitTargetTaskProxyFunction(7876 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,7877 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,7878 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {7879 7880 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.7881 // This is because PrivatesTy is the type of the structure in which7882 // we pass the offloading arrays to the deferred target task.7883 assert((!NumOffloadingArrays || PrivatesTy) &&7884 "PrivatesTy cannot be nullptr when there are offloadingArrays"7885 "to privatize");7886 7887 Module &M = OMPBuilder.M;7888 // KernelLaunchFunction is the target launch function, i.e.7889 // the function that sets up kernel arguments and calls7890 // __tgt_target_kernel to launch the kernel on the device.7891 //7892 Function *KernelLaunchFunction = StaleCI->getCalledFunction();7893 7894 // StaleCI is the CallInst which is the call to the outlined7895 // target kernel launch function. If there are local live-in values7896 // that the outlined function uses then these are aggregated into a structure7897 // which is passed as the second argument. If there are no local live-in7898 // values or if all values used by the outlined kernel are global variables,7899 // then there's only one argument, the threadID. So, StaleCI can be7900 //7901 // %structArg = alloca { ptr, ptr }, align 87902 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 07903 // store ptr %20, ptr %gep_, align 87904 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 17905 // store ptr %21, ptr %gep_8, align 87906 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)7907 //7908 // OR7909 //7910 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)7911 OpenMPIRBuilder::InsertPointTy IP(StaleCI->getParent(),7912 StaleCI->getIterator());7913 7914 LLVMContext &Ctx = StaleCI->getParent()->getContext();7915 7916 Type *ThreadIDTy = Type::getInt32Ty(Ctx);7917 Type *TaskPtrTy = OMPBuilder.TaskPtr;7918 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;7919 7920 auto ProxyFnTy =7921 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},7922 /* isVarArg */ false);7923 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,7924 ".omp_target_task_proxy_func",7925 Builder.GetInsertBlock()->getModule());7926 Value *ThreadId = ProxyFn->getArg(0);7927 Value *TaskWithPrivates = ProxyFn->getArg(1);7928 ThreadId->setName("thread.id");7929 TaskWithPrivates->setName("task");7930 7931 bool HasShareds = SharedArgsOperandNo > 0;7932 bool HasOffloadingArrays = NumOffloadingArrays > 0;7933 BasicBlock *EntryBB =7934 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);7935 Builder.SetInsertPoint(EntryBB);7936 7937 SmallVector<Value *> KernelLaunchArgs;7938 KernelLaunchArgs.reserve(StaleCI->arg_size());7939 KernelLaunchArgs.push_back(ThreadId);7940 7941 if (HasOffloadingArrays) {7942 assert(TaskTy != TaskWithPrivatesTy &&7943 "If there are offloading arrays to pass to the target"7944 "TaskTy cannot be the same as TaskWithPrivatesTy");7945 (void)TaskTy;7946 Value *Privates =7947 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);7948 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)7949 KernelLaunchArgs.push_back(7950 Builder.CreateStructGEP(PrivatesTy, Privates, i));7951 }7952 7953 if (HasShareds) {7954 auto *ArgStructAlloca =7955 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));7956 assert(ArgStructAlloca &&7957 "Unable to find the alloca instruction corresponding to arguments "7958 "for extracted function");7959 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());7960 7961 AllocaInst *NewArgStructAlloca =7962 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");7963 7964 Value *SharedsSize =7965 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ArgStructType));7966 7967 LoadInst *LoadShared = loadSharedDataFromTaskDescriptor(7968 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);7969 7970 Builder.CreateMemCpy(7971 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,7972 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);7973 KernelLaunchArgs.push_back(NewArgStructAlloca);7974 }7975 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);7976 Builder.CreateRetVoid();7977 return ProxyFn;7978}7979static Type *getOffloadingArrayType(Value *V) {7980 7981 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))7982 return GEP->getSourceElementType();7983 if (auto *Alloca = dyn_cast<AllocaInst>(V))7984 return Alloca->getAllocatedType();7985 7986 llvm_unreachable("Unhandled Instruction type");7987 return nullptr;7988}7989// This function returns a struct that has at most two members.7990// The first member is always %struct.kmp_task_ompbuilder_t, that is the task7991// descriptor. The second member, if needed, is a struct containing arrays7992// that need to be passed to the offloaded target kernel. For example,7993// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to7994// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]7995// respectively, then the types created by this function are7996//7997// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }7998// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,7999// %struct.privates }8000// %struct.task_with_privates is returned by this function.8001// If there aren't any offloading arrays to pass to the target kernel,8002// %struct.kmp_task_ompbuilder_t is returned.8003static StructType *8004createTaskWithPrivatesTy(OpenMPIRBuilder &OMPIRBuilder,8005 ArrayRef<Value *> OffloadingArraysToPrivatize) {8006 8007 if (OffloadingArraysToPrivatize.empty())8008 return OMPIRBuilder.Task;8009 8010 SmallVector<Type *, 4> StructFieldTypes;8011 for (Value *V : OffloadingArraysToPrivatize) {8012 assert(V->getType()->isPointerTy() &&8013 "Expected pointer to array to privatize. Got a non-pointer value "8014 "instead");8015 Type *ArrayTy = getOffloadingArrayType(V);8016 assert(ArrayTy && "ArrayType cannot be nullptr");8017 StructFieldTypes.push_back(ArrayTy);8018 }8019 StructType *PrivatesStructTy =8020 StructType::create(StructFieldTypes, "struct.privates");8021 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},8022 "struct.task_with_privates");8023}8024static Error emitTargetOutlinedFunction(8025 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,8026 TargetRegionEntryInfo &EntryInfo,8027 const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs,8028 Function *&OutlinedFn, Constant *&OutlinedFnID,8029 SmallVectorImpl<Value *> &Inputs,8030 OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc,8031 OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB) {8032 8033 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =8034 [&](StringRef EntryFnName) {8035 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,8036 EntryFnName, Inputs, CBFunc,8037 ArgAccessorFuncCB);8038 };8039 8040 return OMPBuilder.emitTargetRegionFunction(8041 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,8042 OutlinedFnID);8043}8044 8045OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitTargetTask(8046 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,8047 OpenMPIRBuilder::InsertPointTy AllocaIP,8048 const SmallVector<llvm::OpenMPIRBuilder::DependData> &Dependencies,8049 const TargetDataRTArgs &RTArgs, bool HasNoWait) {8050 8051 // The following explains the code-gen scenario for the `target` directive. A8052 // similar scneario is followed for other device-related directives (e.g.8053 // `target enter data`) but in similar fashion since we only need to emit task8054 // that encapsulates the proper runtime call.8055 //8056 // When we arrive at this function, the target region itself has been8057 // outlined into the function OutlinedFn.8058 // So at ths point, for8059 // --------------------------------------------------------------8060 // void user_code_that_offloads(...) {8061 // omp target depend(..) map(from:a) map(to:b) private(i)8062 // do i = 1, 108063 // a(i) = b(i) + n8064 // }8065 //8066 // --------------------------------------------------------------8067 //8068 // we have8069 //8070 // --------------------------------------------------------------8071 //8072 // void user_code_that_offloads(...) {8073 // %.offload_baseptrs = alloca [2 x ptr], align 88074 // %.offload_ptrs = alloca [2 x ptr], align 88075 // %.offload_mappers = alloca [2 x ptr], align 88076 // ;; target region has been outlined and now we need to8077 // ;; offload to it via a target task.8078 // }8079 // void outlined_device_function(ptr a, ptr b, ptr n) {8080 // n = *n_ptr;8081 // do i = 1, 108082 // a(i) = b(i) + n8083 // }8084 //8085 // We have to now do the following8086 // (i) Make an offloading call to outlined_device_function using the OpenMP8087 // RTL. See 'kernel_launch_function' in the pseudo code below. This is8088 // emitted by emitKernelLaunch8089 // (ii) Create a task entry point function that calls kernel_launch_function8090 // and is the entry point for the target task. See8091 // '@.omp_target_task_proxy_func in the pseudocode below.8092 // (iii) Create a task with the task entry point created in (ii)8093 //8094 // That is we create the following8095 // struct task_with_privates {8096 // struct kmp_task_ompbuilder_t task_struct;8097 // struct privates {8098 // [2 x ptr] ; baseptrs8099 // [2 x ptr] ; ptrs8100 // [2 x i64] ; sizes8101 // }8102 // }8103 // void user_code_that_offloads(...) {8104 // %.offload_baseptrs = alloca [2 x ptr], align 88105 // %.offload_ptrs = alloca [2 x ptr], align 88106 // %.offload_sizes = alloca [2 x i64], align 88107 //8108 // %structArg = alloca { ptr, ptr, ptr }, align 88109 // %strucArg[0] = a8110 // %strucArg[1] = b8111 // %strucArg[2] = &n8112 //8113 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,8114 // sizeof(kmp_task_ompbuilder_t),8115 // sizeof(structArg),8116 // @.omp_target_task_proxy_func,8117 // ...)8118 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,8119 // sizeof(structArg))8120 // memcpy(target_task_with_privates->privates->baseptrs,8121 // offload_baseptrs, sizeof(offload_baseptrs)8122 // memcpy(target_task_with_privates->privates->ptrs,8123 // offload_ptrs, sizeof(offload_ptrs)8124 // memcpy(target_task_with_privates->privates->sizes,8125 // offload_sizes, sizeof(offload_sizes)8126 // dependencies_array = ...8127 // ;; if nowait not present8128 // call @__kmpc_omp_wait_deps(..., dependencies_array)8129 // call @__kmpc_omp_task_begin_if0(...)8130 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr8131 // %target_task_with_privates)8132 // call @__kmpc_omp_task_complete_if0(...)8133 // }8134 //8135 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,8136 // ptr %task) {8137 // %structArg = alloca {ptr, ptr, ptr}8138 // %task_ptr = getelementptr(%task, 0, 0)8139 // %shared_data = load (getelementptr %task_ptr, 0, 0)8140 // mempcy(%structArg, %shared_data, sizeof(%structArg))8141 //8142 // %offloading_arrays = getelementptr(%task, 0, 1)8143 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)8144 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)8145 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)8146 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,8147 // %offload_sizes, %structArg)8148 // }8149 //8150 // We need the proxy function because the signature of the task entry point8151 // expected by kmpc_omp_task is always the same and will be different from8152 // that of the kernel_launch function.8153 //8154 // kernel_launch_function is generated by emitKernelLaunch and has the8155 // always_inline attribute. For this example, it'll look like so:8156 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,8157 // %offload_sizes, %structArg) alwaysinline {8158 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 88159 // ; load aggregated data from %structArg8160 // ; setup kernel_args using offload_baseptrs, offload_ptrs and8161 // ; offload_sizes8162 // call i32 @__tgt_target_kernel(...,8163 // outlined_device_function,8164 // ptr %kernel_args)8165 // }8166 // void outlined_device_function(ptr a, ptr b, ptr n) {8167 // n = *n_ptr;8168 // do i = 1, 108169 // a(i) = b(i) + n8170 // }8171 //8172 BasicBlock *TargetTaskBodyBB =8173 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");8174 BasicBlock *TargetTaskAllocaBB =8175 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");8176 8177 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB,8178 TargetTaskAllocaBB->begin());8179 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB, TargetTaskBodyBB->begin());8180 8181 OutlineInfo OI;8182 OI.EntryBB = TargetTaskAllocaBB;8183 OI.OuterAllocaBB = AllocaIP.getBlock();8184 8185 // Add the thread ID argument.8186 SmallVector<Instruction *, 4> ToBeDeleted;8187 OI.ExcludeArgsFromAggregate.push_back(createFakeIntVal(8188 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));8189 8190 // Generate the task body which will subsequently be outlined.8191 Builder.restoreIP(TargetTaskBodyIP);8192 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))8193 return Err;8194 8195 // The outliner (CodeExtractor) extract a sequence or vector of blocks that8196 // it is given. These blocks are enumerated by8197 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock8198 // to be outside the region. In other words, OI.ExitBlock is expected to be8199 // the start of the region after the outlining. We used to set OI.ExitBlock8200 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases8201 // except when the task body is a single basic block. In that case,8202 // OI.ExitBlock is set to the single task body block and will get left out of8203 // the outlining process. So, simply create a new empty block to which we8204 // uncoditionally branch from where TaskBodyCB left off8205 OI.ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");8206 emitBlock(OI.ExitBB, Builder.GetInsertBlock()->getParent(),8207 /*IsFinished=*/true);8208 8209 SmallVector<Value *, 2> OffloadingArraysToPrivatize;8210 bool NeedsTargetTask = HasNoWait && DeviceID;8211 if (NeedsTargetTask) {8212 for (auto *V :8213 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,8214 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,8215 RTArgs.SizesArray}) {8216 if (V && !isa<ConstantPointerNull, GlobalVariable>(V)) {8217 OffloadingArraysToPrivatize.push_back(V);8218 OI.ExcludeArgsFromAggregate.push_back(V);8219 }8220 }8221 }8222 OI.PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,8223 DeviceID, OffloadingArraysToPrivatize](8224 Function &OutlinedFn) mutable {8225 assert(OutlinedFn.hasOneUse() &&8226 "there must be a single user for the outlined function");8227 8228 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());8229 8230 // The first argument of StaleCI is always the thread id.8231 // The next few arguments are the pointers to offloading arrays8232 // if any. (see OffloadingArraysToPrivatize)8233 // Finally, all other local values that are live-in into the outlined region8234 // end up in a structure whose pointer is passed as the last argument. This8235 // piece of data is passed in the "shared" field of the task structure. So,8236 // we know we have to pass shareds to the task if the number of arguments is8237 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the8238 // thread id. Further, for safety, we assert that the number of arguments of8239 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 28240 const unsigned int NumStaleCIArgs = StaleCI->arg_size();8241 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;8242 assert((!HasShareds ||8243 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&8244 "Wrong number of arguments for StaleCI when shareds are present");8245 int SharedArgOperandNo =8246 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;8247 8248 StructType *TaskWithPrivatesTy =8249 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);8250 StructType *PrivatesTy = nullptr;8251 8252 if (!OffloadingArraysToPrivatize.empty())8253 PrivatesTy =8254 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));8255 8256 Function *ProxyFn = emitTargetTaskProxyFunction(8257 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,8258 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);8259 8260 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn8261 << "\n");8262 8263 Builder.SetInsertPoint(StaleCI);8264 8265 // Gather the arguments for emitting the runtime call.8266 uint32_t SrcLocStrSize;8267 Constant *SrcLocStr =8268 getOrCreateSrcLocStr(LocationDescription(Builder), SrcLocStrSize);8269 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);8270 8271 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc8272 //8273 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide8274 // the DeviceID to the deferred task and also since8275 // @__kmpc_omp_target_task_alloc creates an untied/async task.8276 Function *TaskAllocFn =8277 !NeedsTargetTask8278 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)8279 : getOrCreateRuntimeFunctionPtr(8280 OMPRTL___kmpc_omp_target_task_alloc);8281 8282 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)8283 // call.8284 Value *ThreadID = getOrCreateThreadID(Ident);8285 8286 // Argument - `sizeof_kmp_task_t` (TaskSize)8287 // Tasksize refers to the size in bytes of kmp_task_t data structure8288 // plus any other data to be passed to the target task, if any, which8289 // is packed into a struct. kmp_task_t and the struct so created are8290 // packed into a wrapper struct whose type is TaskWithPrivatesTy.8291 Value *TaskSize = Builder.getInt64(8292 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));8293 8294 // Argument - `sizeof_shareds` (SharedsSize)8295 // SharedsSize refers to the shareds array size in the kmp_task_t data8296 // structure.8297 Value *SharedsSize = Builder.getInt64(0);8298 if (HasShareds) {8299 auto *ArgStructAlloca =8300 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));8301 assert(ArgStructAlloca &&8302 "Unable to find the alloca instruction corresponding to arguments "8303 "for extracted function");8304 auto *ArgStructType =8305 dyn_cast<StructType>(ArgStructAlloca->getAllocatedType());8306 assert(ArgStructType && "Unable to find struct type corresponding to "8307 "arguments for extracted function");8308 SharedsSize =8309 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ArgStructType));8310 }8311 8312 // Argument - `flags`8313 // Task is tied iff (Flags & 1) == 1.8314 // Task is untied iff (Flags & 1) == 0.8315 // Task is final iff (Flags & 2) == 2.8316 // Task is not final iff (Flags & 2) == 0.8317 // A target task is not final and is untied.8318 Value *Flags = Builder.getInt32(0);8319 8320 // Emit the @__kmpc_omp_task_alloc runtime call8321 // The runtime call returns a pointer to an area where the task captured8322 // variables must be copied before the task is run (TaskData)8323 CallInst *TaskData = nullptr;8324 8325 SmallVector<llvm::Value *> TaskAllocArgs = {8326 /*loc_ref=*/Ident, /*gtid=*/ThreadID,8327 /*flags=*/Flags,8328 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,8329 /*task_func=*/ProxyFn};8330 8331 if (NeedsTargetTask) {8332 assert(DeviceID && "Expected non-empty device ID.");8333 TaskAllocArgs.push_back(DeviceID);8334 }8335 8336 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);8337 8338 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());8339 if (HasShareds) {8340 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);8341 Value *TaskShareds = loadSharedDataFromTaskDescriptor(8342 *this, Builder, TaskData, TaskWithPrivatesTy);8343 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,8344 SharedsSize);8345 }8346 if (!OffloadingArraysToPrivatize.empty()) {8347 Value *Privates =8348 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);8349 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {8350 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];8351 [[maybe_unused]] Type *ArrayType =8352 getOffloadingArrayType(PtrToPrivatize);8353 assert(ArrayType && "ArrayType cannot be nullptr");8354 8355 Type *ElementType = PrivatesTy->getElementType(i);8356 assert(ElementType == ArrayType &&8357 "ElementType should match ArrayType");8358 (void)ArrayType;8359 8360 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);8361 Builder.CreateMemCpy(8362 Dst, Alignment, PtrToPrivatize, Alignment,8363 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));8364 }8365 }8366 8367 Value *DepArray = emitTaskDependencies(*this, Dependencies);8368 8369 // ---------------------------------------------------------------8370 // V5.2 13.8 target construct8371 // If the nowait clause is present, execution of the target task8372 // may be deferred. If the nowait clause is not present, the target task is8373 // an included task.8374 // ---------------------------------------------------------------8375 // The above means that the lack of a nowait on the target construct8376 // translates to '#pragma omp task if(0)'8377 if (!NeedsTargetTask) {8378 if (DepArray) {8379 Function *TaskWaitFn =8380 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);8381 createRuntimeFunctionCall(8382 TaskWaitFn,8383 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,8384 /*ndeps=*/Builder.getInt32(Dependencies.size()),8385 /*dep_list=*/DepArray,8386 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),8387 /*noalias_dep_list=*/8388 ConstantPointerNull::get(PointerType::getUnqual(M.getContext()))});8389 }8390 // Included task.8391 Function *TaskBeginFn =8392 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);8393 Function *TaskCompleteFn =8394 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);8395 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});8396 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});8397 CI->setDebugLoc(StaleCI->getDebugLoc());8398 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});8399 } else if (DepArray) {8400 // HasNoWait - meaning the task may be deferred. Call8401 // __kmpc_omp_task_with_deps if there are dependencies,8402 // else call __kmpc_omp_task8403 Function *TaskFn =8404 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);8405 createRuntimeFunctionCall(8406 TaskFn,8407 {Ident, ThreadID, TaskData, Builder.getInt32(Dependencies.size()),8408 DepArray, ConstantInt::get(Builder.getInt32Ty(), 0),8409 ConstantPointerNull::get(PointerType::getUnqual(M.getContext()))});8410 } else {8411 // Emit the @__kmpc_omp_task runtime call to spawn the task8412 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);8413 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});8414 }8415 8416 StaleCI->eraseFromParent();8417 for (Instruction *I : llvm::reverse(ToBeDeleted))8418 I->eraseFromParent();8419 };8420 addOutlineInfo(std::move(OI));8421 8422 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"8423 << *(Builder.GetInsertBlock()) << "\n");8424 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"8425 << *(Builder.GetInsertBlock()->getParent()->getParent())8426 << "\n");8427 return Builder.saveIP();8428}8429 8430Error OpenMPIRBuilder::emitOffloadingArraysAndArgs(8431 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,8432 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,8433 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,8434 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {8435 if (Error Err =8436 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,8437 CustomMapperCB, IsNonContiguous, DeviceAddrCB))8438 return Err;8439 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);8440 return Error::success();8441}8442 8443static void emitTargetCall(8444 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,8445 OpenMPIRBuilder::InsertPointTy AllocaIP,8446 OpenMPIRBuilder::TargetDataInfo &Info,8447 const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs,8448 const OpenMPIRBuilder::TargetKernelRuntimeAttrs &RuntimeAttrs,8449 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,8450 SmallVectorImpl<Value *> &Args,8451 OpenMPIRBuilder::GenMapInfoCallbackTy GenMapInfoCB,8452 OpenMPIRBuilder::CustomMapperCallbackTy CustomMapperCB,8453 const SmallVector<llvm::OpenMPIRBuilder::DependData> &Dependencies,8454 bool HasNoWait, Value *DynCGroupMem,8455 OMPDynGroupprivateFallbackType DynCGroupMemFallback) {8456 // Generate a function call to the host fallback implementation of the target8457 // region. This is called by the host when no offload entry was generated for8458 // the target region and when the offloading call fails at runtime.8459 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)8460 -> OpenMPIRBuilder::InsertPointOrErrorTy {8461 Builder.restoreIP(IP);8462 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, Args);8463 return Builder.saveIP();8464 };8465 8466 bool HasDependencies = Dependencies.size() > 0;8467 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;8468 8469 OpenMPIRBuilder::TargetKernelArgs KArgs;8470 8471 auto TaskBodyCB =8472 [&](Value *DeviceID, Value *RTLoc,8473 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {8474 // Assume no error was returned because EmitTargetCallFallbackCB doesn't8475 // produce any.8476 llvm::OpenMPIRBuilder::InsertPointTy AfterIP = cantFail([&]() {8477 // emitKernelLaunch makes the necessary runtime call to offload the8478 // kernel. We then outline all that code into a separate function8479 // ('kernel_launch_function' in the pseudo code above). This function is8480 // then called by the target task proxy function (see8481 // '@.omp_target_task_proxy_func' in the pseudo code above)8482 // "@.omp_target_task_proxy_func' is generated by8483 // emitTargetTaskProxyFunction.8484 if (OutlinedFnID && DeviceID)8485 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,8486 EmitTargetCallFallbackCB, KArgs,8487 DeviceID, RTLoc, TargetTaskAllocaIP);8488 8489 // We only need to do the outlining if `DeviceID` is set to avoid calling8490 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are8491 // generating the `else` branch of an `if` clause.8492 //8493 // When OutlinedFnID is set to nullptr, then it's not an offloading call.8494 // In this case, we execute the host implementation directly.8495 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());8496 }());8497 8498 OMPBuilder.Builder.restoreIP(AfterIP);8499 return Error::success();8500 };8501 8502 auto &&EmitTargetCallElse =8503 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,8504 OpenMPIRBuilder::InsertPointTy CodeGenIP) -> Error {8505 // Assume no error was returned because EmitTargetCallFallbackCB doesn't8506 // produce any.8507 OpenMPIRBuilder::InsertPointTy AfterIP = cantFail([&]() {8508 if (RequiresOuterTargetTask) {8509 // Arguments that are intended to be directly forwarded to an8510 // emitKernelLaunch call are pased as nullptr, since8511 // OutlinedFnID=nullptr results in that call not being done.8512 OpenMPIRBuilder::TargetDataRTArgs EmptyRTArgs;8513 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,8514 /*RTLoc=*/nullptr, AllocaIP,8515 Dependencies, EmptyRTArgs, HasNoWait);8516 }8517 return EmitTargetCallFallbackCB(Builder.saveIP());8518 }());8519 8520 Builder.restoreIP(AfterIP);8521 return Error::success();8522 };8523 8524 auto &&EmitTargetCallThen =8525 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,8526 OpenMPIRBuilder::InsertPointTy CodeGenIP) -> Error {8527 Info.HasNoWait = HasNoWait;8528 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());8529 OpenMPIRBuilder::TargetDataRTArgs RTArgs;8530 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(8531 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,8532 /*IsNonContiguous=*/true,8533 /*ForEndCall=*/false))8534 return Err;8535 8536 SmallVector<Value *, 3> NumTeamsC;8537 for (auto [DefaultVal, RuntimeVal] :8538 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))8539 NumTeamsC.push_back(RuntimeVal ? RuntimeVal8540 : Builder.getInt32(DefaultVal));8541 8542 // Calculate number of threads: 0 if no clauses specified, otherwise it is8543 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.8544 auto InitMaxThreadsClause = [&Builder](Value *Clause) {8545 if (Clause)8546 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),8547 /*isSigned=*/false);8548 return Clause;8549 };8550 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {8551 if (Clause)8552 Result =8553 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),8554 Result, Clause)8555 : Clause;8556 };8557 8558 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so8559 // the NUM_THREADS clause is overriden by THREAD_LIMIT.8560 SmallVector<Value *, 3> NumThreadsC;8561 Value *MaxThreadsClause =8562 RuntimeAttrs.TeamsThreadLimit.size() == 18563 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads)8564 : nullptr;8565 8566 for (auto [TeamsVal, TargetVal] : zip_equal(8567 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {8568 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);8569 Value *NumThreads = InitMaxThreadsClause(TargetVal);8570 8571 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);8572 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);8573 8574 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));8575 }8576 8577 unsigned NumTargetItems = Info.NumberOfPtrs;8578 // TODO: Use correct device ID8579 Value *DeviceID = Builder.getInt64(OMP_DEVICEID_UNDEF);8580 uint32_t SrcLocStrSize;8581 Constant *SrcLocStr = OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);8582 Value *RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,8583 llvm::omp::IdentFlag(0), 0);8584 8585 Value *TripCount = RuntimeAttrs.LoopTripCount8586 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,8587 Builder.getInt64Ty(),8588 /*isSigned=*/false)8589 : Builder.getInt64(0);8590 8591 // Request zero groupprivate bytes by default.8592 if (!DynCGroupMem)8593 DynCGroupMem = Builder.getInt32(0);8594 8595 KArgs = OpenMPIRBuilder::TargetKernelArgs(8596 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,8597 HasNoWait, DynCGroupMemFallback);8598 8599 // Assume no error was returned because TaskBodyCB and8600 // EmitTargetCallFallbackCB don't produce any.8601 OpenMPIRBuilder::InsertPointTy AfterIP = cantFail([&]() {8602 // The presence of certain clauses on the target directive require the8603 // explicit generation of the target task.8604 if (RequiresOuterTargetTask)8605 return OMPBuilder.emitTargetTask(TaskBodyCB, DeviceID, RTLoc, AllocaIP,8606 Dependencies, KArgs.RTArgs,8607 Info.HasNoWait);8608 8609 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,8610 EmitTargetCallFallbackCB, KArgs,8611 DeviceID, RTLoc, AllocaIP);8612 }());8613 8614 Builder.restoreIP(AfterIP);8615 return Error::success();8616 };8617 8618 // If we don't have an ID for the target region, it means an offload entry8619 // wasn't created. In this case we just run the host fallback directly and8620 // ignore any potential 'if' clauses.8621 if (!OutlinedFnID) {8622 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP()));8623 return;8624 }8625 8626 // If there's no 'if' clause, only generate the kernel launch code path.8627 if (!IfCond) {8628 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP()));8629 return;8630 }8631 8632 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,8633 EmitTargetCallElse, AllocaIP));8634}8635 8636OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTarget(8637 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,8638 InsertPointTy CodeGenIP, TargetDataInfo &Info,8639 TargetRegionEntryInfo &EntryInfo,8640 const TargetKernelDefaultAttrs &DefaultAttrs,8641 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,8642 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,8643 OpenMPIRBuilder::TargetBodyGenCallbackTy CBFunc,8644 OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy ArgAccessorFuncCB,8645 CustomMapperCallbackTy CustomMapperCB,8646 const SmallVector<DependData> &Dependencies, bool HasNowait,8647 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {8648 8649 if (!updateToLocation(Loc))8650 return InsertPointTy();8651 8652 Builder.restoreIP(CodeGenIP);8653 8654 Function *OutlinedFn;8655 Constant *OutlinedFnID = nullptr;8656 // The target region is outlined into its own function. The LLVM IR for8657 // the target region itself is generated using the callbacks CBFunc8658 // and ArgAccessorFuncCB8659 if (Error Err = emitTargetOutlinedFunction(8660 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,8661 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB))8662 return Err;8663 8664 // If we are not on the target device, then we need to generate code8665 // to make a remote call (offload) to the previously outlined function8666 // that represents the target region. Do that now.8667 if (!Config.isTargetDevice())8668 emitTargetCall(*this, Builder, AllocaIP, Info, DefaultAttrs, RuntimeAttrs,8669 IfCond, OutlinedFn, OutlinedFnID, Inputs, GenMapInfoCB,8670 CustomMapperCB, Dependencies, HasNowait, DynCGroupMem,8671 DynCGroupMemFallback);8672 return Builder.saveIP();8673}8674 8675std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,8676 StringRef FirstSeparator,8677 StringRef Separator) {8678 SmallString<128> Buffer;8679 llvm::raw_svector_ostream OS(Buffer);8680 StringRef Sep = FirstSeparator;8681 for (StringRef Part : Parts) {8682 OS << Sep << Part;8683 Sep = Separator;8684 }8685 return OS.str().str();8686}8687 8688std::string8689OpenMPIRBuilder::createPlatformSpecificName(ArrayRef<StringRef> Parts) const {8690 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),8691 Config.separator());8692}8693 8694GlobalVariable *OpenMPIRBuilder::getOrCreateInternalVariable(8695 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {8696 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;8697 if (Elem.second) {8698 assert(Elem.second->getValueType() == Ty &&8699 "OMP internal variable has different type than requested");8700 } else {8701 // TODO: investigate the appropriate linkage type used for the global8702 // variable for possibly changing that to internal or private, or maybe8703 // create different versions of the function for different OMP internal8704 // variables.8705 const DataLayout &DL = M.getDataLayout();8706 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the8707 // default global AS is 1.8708 // See double-target-call-with-declare-target.f90 and8709 // declare-target-vars-in-target-region.f90 libomptarget8710 // tests.8711 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace8712 : M.getTargetTriple().isAMDGPU()8713 ? 08714 : DL.getDefaultGlobalsAddressSpace();8715 auto Linkage = this->M.getTargetTriple().getArch() == Triple::wasm328716 ? GlobalValue::InternalLinkage8717 : GlobalValue::CommonLinkage;8718 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,8719 Constant::getNullValue(Ty), Elem.first(),8720 /*InsertBefore=*/nullptr,8721 GlobalValue::NotThreadLocal, AddressSpaceVal);8722 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);8723 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);8724 GV->setAlignment(std::max(TypeAlign, PtrAlign));8725 Elem.second = GV;8726 }8727 8728 return Elem.second;8729}8730 8731Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {8732 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();8733 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");8734 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);8735}8736 8737Value *OpenMPIRBuilder::getSizeInBytes(Value *BasePtr) {8738 LLVMContext &Ctx = Builder.getContext();8739 Value *Null =8740 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));8741 Value *SizeGep =8742 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));8743 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));8744 return SizePtrToInt;8745}8746 8747GlobalVariable *8748OpenMPIRBuilder::createOffloadMaptypes(SmallVectorImpl<uint64_t> &Mappings,8749 std::string VarName) {8750 llvm::Constant *MaptypesArrayInit =8751 llvm::ConstantDataArray::get(M.getContext(), Mappings);8752 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(8753 M, MaptypesArrayInit->getType(),8754 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,8755 VarName);8756 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);8757 return MaptypesArrayGlobal;8758}8759 8760void OpenMPIRBuilder::createMapperAllocas(const LocationDescription &Loc,8761 InsertPointTy AllocaIP,8762 unsigned NumOperands,8763 struct MapperAllocas &MapperAllocas) {8764 if (!updateToLocation(Loc))8765 return;8766 8767 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);8768 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);8769 Builder.restoreIP(AllocaIP);8770 AllocaInst *ArgsBase = Builder.CreateAlloca(8771 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");8772 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,8773 ".offload_ptrs");8774 AllocaInst *ArgSizes = Builder.CreateAlloca(8775 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");8776 updateToLocation(Loc);8777 MapperAllocas.ArgsBase = ArgsBase;8778 MapperAllocas.Args = Args;8779 MapperAllocas.ArgSizes = ArgSizes;8780}8781 8782void OpenMPIRBuilder::emitMapperCall(const LocationDescription &Loc,8783 Function *MapperFunc, Value *SrcLocInfo,8784 Value *MaptypesArg, Value *MapnamesArg,8785 struct MapperAllocas &MapperAllocas,8786 int64_t DeviceID, unsigned NumOperands) {8787 if (!updateToLocation(Loc))8788 return;8789 8790 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);8791 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);8792 Value *ArgsBaseGEP =8793 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,8794 {Builder.getInt32(0), Builder.getInt32(0)});8795 Value *ArgsGEP =8796 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,8797 {Builder.getInt32(0), Builder.getInt32(0)});8798 Value *ArgSizesGEP =8799 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,8800 {Builder.getInt32(0), Builder.getInt32(0)});8801 Value *NullPtr =8802 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));8803 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),8804 Builder.getInt32(NumOperands),8805 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,8806 MaptypesArg, MapnamesArg, NullPtr});8807}8808 8809void OpenMPIRBuilder::emitOffloadingArraysArgument(IRBuilderBase &Builder,8810 TargetDataRTArgs &RTArgs,8811 TargetDataInfo &Info,8812 bool ForEndCall) {8813 assert((!ForEndCall || Info.separateBeginEndCalls()) &&8814 "expected region end call to runtime only when end call is separate");8815 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());8816 auto VoidPtrTy = UnqualPtrTy;8817 auto VoidPtrPtrTy = UnqualPtrTy;8818 auto Int64Ty = Type::getInt64Ty(M.getContext());8819 auto Int64PtrTy = UnqualPtrTy;8820 8821 if (!Info.NumberOfPtrs) {8822 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);8823 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);8824 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);8825 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);8826 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);8827 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);8828 return;8829 }8830 8831 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(8832 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),8833 Info.RTArgs.BasePointersArray,8834 /*Idx0=*/0, /*Idx1=*/0);8835 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(8836 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,8837 /*Idx0=*/0,8838 /*Idx1=*/0);8839 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(8840 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,8841 /*Idx0=*/0, /*Idx1=*/0);8842 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(8843 ArrayType::get(Int64Ty, Info.NumberOfPtrs),8844 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd8845 : Info.RTArgs.MapTypesArray,8846 /*Idx0=*/0,8847 /*Idx1=*/0);8848 8849 // Only emit the mapper information arrays if debug information is8850 // requested.8851 if (!Info.EmitDebug)8852 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);8853 else8854 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(8855 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,8856 /*Idx0=*/0,8857 /*Idx1=*/0);8858 // If there is no user-defined mapper, set the mapper array to nullptr to8859 // avoid an unnecessary data privatization8860 if (!Info.HasMapper)8861 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);8862 else8863 RTArgs.MappersArray =8864 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);8865}8866 8867void OpenMPIRBuilder::emitNonContiguousDescriptor(InsertPointTy AllocaIP,8868 InsertPointTy CodeGenIP,8869 MapInfosTy &CombinedInfo,8870 TargetDataInfo &Info) {8871 MapInfosTy::StructNonContiguousInfo &NonContigInfo =8872 CombinedInfo.NonContigInfo;8873 8874 // Build an array of struct descriptor_dim and then assign it to8875 // offload_args.8876 //8877 // struct descriptor_dim {8878 // uint64_t offset;8879 // uint64_t count;8880 // uint64_t stride8881 // };8882 Type *Int64Ty = Builder.getInt64Ty();8883 StructType *DimTy = StructType::create(8884 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),8885 "struct.descriptor_dim");8886 8887 enum { OffsetFD = 0, CountFD, StrideFD };8888 // We need two index variable here since the size of "Dims" is the same as8889 // the size of Components, however, the size of offset, count, and stride is8890 // equal to the size of base declaration that is non-contiguous.8891 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {8892 // Skip emitting ir if dimension size is 1 since it cannot be8893 // non-contiguous.8894 if (NonContigInfo.Dims[I] == 1)8895 continue;8896 Builder.restoreIP(AllocaIP);8897 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);8898 AllocaInst *DimsAddr =8899 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");8900 Builder.restoreIP(CodeGenIP);8901 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {8902 unsigned RevIdx = EE - II - 1;8903 Value *DimsLVal = Builder.CreateInBoundsGEP(8904 DimsAddr->getAllocatedType(), DimsAddr,8905 {Builder.getInt64(0), Builder.getInt64(II)});8906 // Offset8907 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);8908 Builder.CreateAlignedStore(8909 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,8910 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));8911 // Count8912 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);8913 Builder.CreateAlignedStore(8914 NonContigInfo.Counts[L][RevIdx], CountLVal,8915 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));8916 // Stride8917 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);8918 Builder.CreateAlignedStore(8919 NonContigInfo.Strides[L][RevIdx], StrideLVal,8920 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));8921 }8922 // args[I] = &dims8923 Builder.restoreIP(CodeGenIP);8924 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(8925 DimsAddr, Builder.getPtrTy());8926 Value *P = Builder.CreateConstInBoundsGEP2_32(8927 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),8928 Info.RTArgs.PointersArray, 0, I);8929 Builder.CreateAlignedStore(8930 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));8931 ++L;8932 }8933}8934 8935void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(8936 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,8937 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,8938 BasicBlock *ExitBB, bool IsInit) {8939 StringRef Prefix = IsInit ? ".init" : ".del";8940 8941 // Evaluate if this is an array section.8942 BasicBlock *BodyBB = BasicBlock::Create(8943 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));8944 Value *IsArray =8945 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");8946 Value *DeleteBit = Builder.CreateAnd(8947 MapType,8948 Builder.getInt64(8949 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(8950 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));8951 Value *DeleteCond;8952 Value *Cond;8953 if (IsInit) {8954 // base != begin?8955 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);8956 // IsPtrAndObj?8957 Value *PtrAndObjBit = Builder.CreateAnd(8958 MapType,8959 Builder.getInt64(8960 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(8961 OpenMPOffloadMappingFlags::OMP_MAP_PTR_AND_OBJ)));8962 PtrAndObjBit = Builder.CreateIsNotNull(PtrAndObjBit);8963 BaseIsBegin = Builder.CreateAnd(BaseIsBegin, PtrAndObjBit);8964 Cond = Builder.CreateOr(IsArray, BaseIsBegin);8965 DeleteCond = Builder.CreateIsNull(8966 DeleteBit,8967 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));8968 } else {8969 Cond = IsArray;8970 DeleteCond = Builder.CreateIsNotNull(8971 DeleteBit,8972 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));8973 }8974 Cond = Builder.CreateAnd(Cond, DeleteCond);8975 Builder.CreateCondBr(Cond, BodyBB, ExitBB);8976 8977 emitBlock(BodyBB, MapperFn);8978 // Get the array size by multiplying element size and element number (i.e., \p8979 // Size).8980 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));8981 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves8982 // memory allocation/deletion purpose only.8983 Value *MapTypeArg = Builder.CreateAnd(8984 MapType,8985 Builder.getInt64(8986 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(8987 OpenMPOffloadMappingFlags::OMP_MAP_TO |8988 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));8989 MapTypeArg = Builder.CreateOr(8990 MapTypeArg,8991 Builder.getInt64(8992 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(8993 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));8994 8995 // Call the runtime API __tgt_push_mapper_component to fill up the runtime8996 // data structure.8997 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,8998 ArraySize, MapTypeArg, MapName};8999 createRuntimeFunctionCall(9000 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),9001 OffloadingArgs);9002}9003 9004Expected<Function *> OpenMPIRBuilder::emitUserDefinedMapper(9005 function_ref<MapInfosOrErrorTy(InsertPointTy CodeGenIP, llvm::Value *PtrPHI,9006 llvm::Value *BeginArg)>9007 GenMapInfoCB,9008 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB) {9009 SmallVector<Type *> Params;9010 Params.emplace_back(Builder.getPtrTy());9011 Params.emplace_back(Builder.getPtrTy());9012 Params.emplace_back(Builder.getPtrTy());9013 Params.emplace_back(Builder.getInt64Ty());9014 Params.emplace_back(Builder.getInt64Ty());9015 Params.emplace_back(Builder.getPtrTy());9016 9017 auto *FnTy =9018 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);9019 9020 SmallString<64> TyStr;9021 raw_svector_ostream Out(TyStr);9022 Function *MapperFn =9023 Function::Create(FnTy, GlobalValue::InternalLinkage, FuncName, M);9024 MapperFn->addFnAttr(Attribute::NoInline);9025 MapperFn->addFnAttr(Attribute::NoUnwind);9026 MapperFn->addParamAttr(0, Attribute::NoUndef);9027 MapperFn->addParamAttr(1, Attribute::NoUndef);9028 MapperFn->addParamAttr(2, Attribute::NoUndef);9029 MapperFn->addParamAttr(3, Attribute::NoUndef);9030 MapperFn->addParamAttr(4, Attribute::NoUndef);9031 MapperFn->addParamAttr(5, Attribute::NoUndef);9032 9033 // Start the mapper function code generation.9034 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);9035 auto SavedIP = Builder.saveIP();9036 Builder.SetInsertPoint(EntryBB);9037 9038 Value *MapperHandle = MapperFn->getArg(0);9039 Value *BaseIn = MapperFn->getArg(1);9040 Value *BeginIn = MapperFn->getArg(2);9041 Value *Size = MapperFn->getArg(3);9042 Value *MapType = MapperFn->getArg(4);9043 Value *MapName = MapperFn->getArg(5);9044 9045 // Compute the starting and end addresses of array elements.9046 // Prepare common arguments for array initiation and deletion.9047 // Convert the size in bytes into the number of array elements.9048 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);9049 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));9050 Value *PtrBegin = BeginIn;9051 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);9052 9053 // Emit array initiation if this is an array section and \p MapType indicates9054 // that memory allocation is required.9055 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");9056 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,9057 MapType, MapName, ElementSize, HeadBB,9058 /*IsInit=*/true);9059 9060 // Emit a for loop to iterate through SizeArg of elements and map all of them.9061 9062 // Emit the loop header block.9063 emitBlock(HeadBB, MapperFn);9064 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");9065 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");9066 // Evaluate whether the initial condition is satisfied.9067 Value *IsEmpty =9068 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");9069 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);9070 9071 // Emit the loop body block.9072 emitBlock(BodyBB, MapperFn);9073 BasicBlock *LastBB = BodyBB;9074 PHINode *PtrPHI =9075 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");9076 PtrPHI->addIncoming(PtrBegin, HeadBB);9077 9078 // Get map clause information. Fill up the arrays with all mapped variables.9079 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);9080 if (!Info)9081 return Info.takeError();9082 9083 // Call the runtime API __tgt_mapper_num_components to get the number of9084 // pre-existing components.9085 Value *OffloadingArgs[] = {MapperHandle};9086 Value *PreviousSize = createRuntimeFunctionCall(9087 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),9088 OffloadingArgs);9089 Value *ShiftedPreviousSize =9090 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));9091 9092 // Fill up the runtime mapper handle for all components.9093 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {9094 Value *CurBaseArg = Info->BasePointers[I];9095 Value *CurBeginArg = Info->Pointers[I];9096 Value *CurSizeArg = Info->Sizes[I];9097 Value *CurNameArg = Info->Names.size()9098 ? Info->Names[I]9099 : Constant::getNullValue(Builder.getPtrTy());9100 9101 // Extract the MEMBER_OF field from the map type.9102 Value *OriMapType = Builder.getInt64(9103 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(9104 Info->Types[I]));9105 Value *MemberMapType =9106 Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);9107 9108 // Combine the map type inherited from user-defined mapper with that9109 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM9110 // bits of the \a MapType, which is the input argument of the mapper9111 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM9112 // bits of MemberMapType.9113 // [OpenMP 5.0], 1.2.6. map-type decay.9114 // | alloc | to | from | tofrom | release | delete9115 // ----------------------------------------------------------9116 // alloc | alloc | alloc | alloc | alloc | release | delete9117 // to | alloc | to | alloc | to | release | delete9118 // from | alloc | alloc | from | from | release | delete9119 // tofrom | alloc | to | from | tofrom | release | delete9120 Value *LeftToFrom = Builder.CreateAnd(9121 MapType,9122 Builder.getInt64(9123 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(9124 OpenMPOffloadMappingFlags::OMP_MAP_TO |9125 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));9126 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");9127 BasicBlock *AllocElseBB =9128 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");9129 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");9130 BasicBlock *ToElseBB =9131 BasicBlock::Create(M.getContext(), "omp.type.to.else");9132 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");9133 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");9134 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);9135 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);9136 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.9137 emitBlock(AllocBB, MapperFn);9138 Value *AllocMapType = Builder.CreateAnd(9139 MemberMapType,9140 Builder.getInt64(9141 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(9142 OpenMPOffloadMappingFlags::OMP_MAP_TO |9143 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));9144 Builder.CreateBr(EndBB);9145 emitBlock(AllocElseBB, MapperFn);9146 Value *IsTo = Builder.CreateICmpEQ(9147 LeftToFrom,9148 Builder.getInt64(9149 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(9150 OpenMPOffloadMappingFlags::OMP_MAP_TO)));9151 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);9152 // In case of to, clear OMP_MAP_FROM.9153 emitBlock(ToBB, MapperFn);9154 Value *ToMapType = Builder.CreateAnd(9155 MemberMapType,9156 Builder.getInt64(9157 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(9158 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));9159 Builder.CreateBr(EndBB);9160 emitBlock(ToElseBB, MapperFn);9161 Value *IsFrom = Builder.CreateICmpEQ(9162 LeftToFrom,9163 Builder.getInt64(9164 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(9165 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));9166 Builder.CreateCondBr(IsFrom, FromBB, EndBB);9167 // In case of from, clear OMP_MAP_TO.9168 emitBlock(FromBB, MapperFn);9169 Value *FromMapType = Builder.CreateAnd(9170 MemberMapType,9171 Builder.getInt64(9172 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(9173 OpenMPOffloadMappingFlags::OMP_MAP_TO)));9174 // In case of tofrom, do nothing.9175 emitBlock(EndBB, MapperFn);9176 LastBB = EndBB;9177 PHINode *CurMapType =9178 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");9179 CurMapType->addIncoming(AllocMapType, AllocBB);9180 CurMapType->addIncoming(ToMapType, ToBB);9181 CurMapType->addIncoming(FromMapType, FromBB);9182 CurMapType->addIncoming(MemberMapType, ToElseBB);9183 9184 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,9185 CurSizeArg, CurMapType, CurNameArg};9186 9187 auto ChildMapperFn = CustomMapperCB(I);9188 if (!ChildMapperFn)9189 return ChildMapperFn.takeError();9190 if (*ChildMapperFn) {9191 // Call the corresponding mapper function.9192 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)9193 ->setDoesNotThrow();9194 } else {9195 // Call the runtime API __tgt_push_mapper_component to fill up the runtime9196 // data structure.9197 createRuntimeFunctionCall(9198 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),9199 OffloadingArgs);9200 }9201 }9202 9203 // Update the pointer to point to the next element that needs to be mapped,9204 // and check whether we have mapped all elements.9205 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,9206 "omp.arraymap.next");9207 PtrPHI->addIncoming(PtrNext, LastBB);9208 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");9209 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");9210 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);9211 9212 emitBlock(ExitBB, MapperFn);9213 // Emit array deletion if this is an array section and \p MapType indicates9214 // that deletion is required.9215 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,9216 MapType, MapName, ElementSize, DoneBB,9217 /*IsInit=*/false);9218 9219 // Emit the function exit block.9220 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);9221 9222 Builder.CreateRetVoid();9223 Builder.restoreIP(SavedIP);9224 return MapperFn;9225}9226 9227Error OpenMPIRBuilder::emitOffloadingArrays(9228 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,9229 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,9230 bool IsNonContiguous,9231 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {9232 9233 // Reset the array information.9234 Info.clearArrayInfo();9235 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();9236 9237 if (Info.NumberOfPtrs == 0)9238 return Error::success();9239 9240 Builder.restoreIP(AllocaIP);9241 // Detect if we have any capture size requiring runtime evaluation of the9242 // size so that a constant array could be eventually used.9243 ArrayType *PointerArrayType =9244 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);9245 9246 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(9247 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");9248 9249 Info.RTArgs.PointersArray = Builder.CreateAlloca(9250 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");9251 AllocaInst *MappersArray = Builder.CreateAlloca(9252 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");9253 Info.RTArgs.MappersArray = MappersArray;9254 9255 // If we don't have any VLA types or other types that require runtime9256 // evaluation, we can use a constant array for the map sizes, otherwise we9257 // need to fill up the arrays as we do for the pointers.9258 Type *Int64Ty = Builder.getInt64Ty();9259 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),9260 ConstantInt::get(Int64Ty, 0));9261 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());9262 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {9263 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {9264 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {9265 if (IsNonContiguous &&9266 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(9267 CombinedInfo.Types[I] &9268 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG))9269 ConstSizes[I] =9270 ConstantInt::get(Int64Ty, CombinedInfo.NonContigInfo.Dims[I]);9271 else9272 ConstSizes[I] = CI;9273 continue;9274 }9275 }9276 RuntimeSizes.set(I);9277 }9278 9279 if (RuntimeSizes.all()) {9280 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);9281 Info.RTArgs.SizesArray = Builder.CreateAlloca(9282 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");9283 restoreIPandDebugLoc(Builder, CodeGenIP);9284 } else {9285 auto *SizesArrayInit = ConstantArray::get(9286 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);9287 std::string Name = createPlatformSpecificName({"offload_sizes"});9288 auto *SizesArrayGbl =9289 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,9290 GlobalValue::PrivateLinkage, SizesArrayInit, Name);9291 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);9292 9293 if (!RuntimeSizes.any()) {9294 Info.RTArgs.SizesArray = SizesArrayGbl;9295 } else {9296 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);9297 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);9298 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);9299 AllocaInst *Buffer = Builder.CreateAlloca(9300 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");9301 Buffer->setAlignment(OffloadSizeAlign);9302 restoreIPandDebugLoc(Builder, CodeGenIP);9303 Builder.CreateMemCpy(9304 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),9305 SizesArrayGbl, OffloadSizeAlign,9306 Builder.getIntN(9307 IndexSize,9308 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));9309 9310 Info.RTArgs.SizesArray = Buffer;9311 }9312 restoreIPandDebugLoc(Builder, CodeGenIP);9313 }9314 9315 // The map types are always constant so we don't need to generate code to9316 // fill arrays. Instead, we create an array constant.9317 SmallVector<uint64_t, 4> Mapping;9318 for (auto mapFlag : CombinedInfo.Types)9319 Mapping.push_back(9320 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(9321 mapFlag));9322 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});9323 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);9324 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;9325 9326 // The information types are only built if provided.9327 if (!CombinedInfo.Names.empty()) {9328 auto *MapNamesArrayGbl = createOffloadMapnames(9329 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));9330 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;9331 Info.EmitDebug = true;9332 } else {9333 Info.RTArgs.MapNamesArray =9334 Constant::getNullValue(PointerType::getUnqual(Builder.getContext()));9335 Info.EmitDebug = false;9336 }9337 9338 // If there's a present map type modifier, it must not be applied to the end9339 // of a region, so generate a separate map type array in that case.9340 if (Info.separateBeginEndCalls()) {9341 bool EndMapTypesDiffer = false;9342 for (uint64_t &Type : Mapping) {9343 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(9344 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {9345 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(9346 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);9347 EndMapTypesDiffer = true;9348 }9349 }9350 if (EndMapTypesDiffer) {9351 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);9352 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;9353 }9354 }9355 9356 PointerType *PtrTy = Builder.getPtrTy();9357 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {9358 Value *BPVal = CombinedInfo.BasePointers[I];9359 Value *BP = Builder.CreateConstInBoundsGEP2_32(9360 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,9361 0, I);9362 Builder.CreateAlignedStore(BPVal, BP,9363 M.getDataLayout().getPrefTypeAlign(PtrTy));9364 9365 if (Info.requiresDevicePointerInfo()) {9366 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {9367 CodeGenIP = Builder.saveIP();9368 Builder.restoreIP(AllocaIP);9369 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};9370 Builder.restoreIP(CodeGenIP);9371 if (DeviceAddrCB)9372 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);9373 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {9374 Info.DevicePtrInfoMap[BPVal] = {BP, BP};9375 if (DeviceAddrCB)9376 DeviceAddrCB(I, BP);9377 }9378 }9379 9380 Value *PVal = CombinedInfo.Pointers[I];9381 Value *P = Builder.CreateConstInBoundsGEP2_32(9382 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,9383 I);9384 // TODO: Check alignment correct.9385 Builder.CreateAlignedStore(PVal, P,9386 M.getDataLayout().getPrefTypeAlign(PtrTy));9387 9388 if (RuntimeSizes.test(I)) {9389 Value *S = Builder.CreateConstInBoundsGEP2_32(9390 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,9391 /*Idx0=*/0,9392 /*Idx1=*/I);9393 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],9394 Int64Ty,9395 /*isSigned=*/true),9396 S, M.getDataLayout().getPrefTypeAlign(PtrTy));9397 }9398 // Fill up the mapper array.9399 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);9400 Value *MFunc = ConstantPointerNull::get(PtrTy);9401 9402 auto CustomMFunc = CustomMapperCB(I);9403 if (!CustomMFunc)9404 return CustomMFunc.takeError();9405 if (*CustomMFunc)9406 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);9407 9408 Value *MAddr = Builder.CreateInBoundsGEP(9409 MappersArray->getAllocatedType(), MappersArray,9410 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});9411 Builder.CreateAlignedStore(9412 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));9413 }9414 9415 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||9416 Info.NumberOfPtrs == 0)9417 return Error::success();9418 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);9419 return Error::success();9420}9421 9422void OpenMPIRBuilder::emitBranch(BasicBlock *Target) {9423 BasicBlock *CurBB = Builder.GetInsertBlock();9424 9425 if (!CurBB || CurBB->getTerminator()) {9426 // If there is no insert point or the previous block is already9427 // terminated, don't touch it.9428 } else {9429 // Otherwise, create a fall-through branch.9430 Builder.CreateBr(Target);9431 }9432 9433 Builder.ClearInsertionPoint();9434}9435 9436void OpenMPIRBuilder::emitBlock(BasicBlock *BB, Function *CurFn,9437 bool IsFinished) {9438 BasicBlock *CurBB = Builder.GetInsertBlock();9439 9440 // Fall out of the current block (if necessary).9441 emitBranch(BB);9442 9443 if (IsFinished && BB->use_empty()) {9444 BB->eraseFromParent();9445 return;9446 }9447 9448 // Place the block after the current block, if possible, or else at9449 // the end of the function.9450 if (CurBB && CurBB->getParent())9451 CurFn->insert(std::next(CurBB->getIterator()), BB);9452 else9453 CurFn->insert(CurFn->end(), BB);9454 Builder.SetInsertPoint(BB);9455}9456 9457Error OpenMPIRBuilder::emitIfClause(Value *Cond, BodyGenCallbackTy ThenGen,9458 BodyGenCallbackTy ElseGen,9459 InsertPointTy AllocaIP) {9460 // If the condition constant folds and can be elided, try to avoid emitting9461 // the condition and the dead arm of the if/else.9462 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {9463 auto CondConstant = CI->getSExtValue();9464 if (CondConstant)9465 return ThenGen(AllocaIP, Builder.saveIP());9466 9467 return ElseGen(AllocaIP, Builder.saveIP());9468 }9469 9470 Function *CurFn = Builder.GetInsertBlock()->getParent();9471 9472 // Otherwise, the condition did not fold, or we couldn't elide it. Just9473 // emit the conditional branch.9474 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");9475 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");9476 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");9477 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);9478 // Emit the 'then' code.9479 emitBlock(ThenBlock, CurFn);9480 if (Error Err = ThenGen(AllocaIP, Builder.saveIP()))9481 return Err;9482 emitBranch(ContBlock);9483 // Emit the 'else' code if present.9484 // There is no need to emit line number for unconditional branch.9485 emitBlock(ElseBlock, CurFn);9486 if (Error Err = ElseGen(AllocaIP, Builder.saveIP()))9487 return Err;9488 // There is no need to emit line number for unconditional branch.9489 emitBranch(ContBlock);9490 // Emit the continuation block for code after the if.9491 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);9492 return Error::success();9493}9494 9495bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(9496 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {9497 assert(!(AO == AtomicOrdering::NotAtomic ||9498 AO == llvm::AtomicOrdering::Unordered) &&9499 "Unexpected Atomic Ordering.");9500 9501 bool Flush = false;9502 llvm::AtomicOrdering FlushAO = AtomicOrdering::Monotonic;9503 9504 switch (AK) {9505 case Read:9506 if (AO == AtomicOrdering::Acquire || AO == AtomicOrdering::AcquireRelease ||9507 AO == AtomicOrdering::SequentiallyConsistent) {9508 FlushAO = AtomicOrdering::Acquire;9509 Flush = true;9510 }9511 break;9512 case Write:9513 case Compare:9514 case Update:9515 if (AO == AtomicOrdering::Release || AO == AtomicOrdering::AcquireRelease ||9516 AO == AtomicOrdering::SequentiallyConsistent) {9517 FlushAO = AtomicOrdering::Release;9518 Flush = true;9519 }9520 break;9521 case Capture:9522 switch (AO) {9523 case AtomicOrdering::Acquire:9524 FlushAO = AtomicOrdering::Acquire;9525 Flush = true;9526 break;9527 case AtomicOrdering::Release:9528 FlushAO = AtomicOrdering::Release;9529 Flush = true;9530 break;9531 case AtomicOrdering::AcquireRelease:9532 case AtomicOrdering::SequentiallyConsistent:9533 FlushAO = AtomicOrdering::AcquireRelease;9534 Flush = true;9535 break;9536 default:9537 // do nothing - leave silently.9538 break;9539 }9540 }9541 9542 if (Flush) {9543 // Currently Flush RT call still doesn't take memory_ordering, so for when9544 // that happens, this tries to do the resolution of which atomic ordering9545 // to use with but issue the flush call9546 // TODO: pass `FlushAO` after memory ordering support is added9547 (void)FlushAO;9548 emitFlush(Loc);9549 }9550 9551 // for AO == AtomicOrdering::Monotonic and all other case combinations9552 // do nothing9553 return Flush;9554}9555 9556OpenMPIRBuilder::InsertPointTy9557OpenMPIRBuilder::createAtomicRead(const LocationDescription &Loc,9558 AtomicOpValue &X, AtomicOpValue &V,9559 AtomicOrdering AO, InsertPointTy AllocaIP) {9560 if (!updateToLocation(Loc))9561 return Loc.IP;9562 9563 assert(X.Var->getType()->isPointerTy() &&9564 "OMP Atomic expects a pointer to target memory");9565 Type *XElemTy = X.ElemTy;9566 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||9567 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&9568 "OMP atomic read expected a scalar type");9569 9570 Value *XRead = nullptr;9571 9572 if (XElemTy->isIntegerTy()) {9573 LoadInst *XLD =9574 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");9575 XLD->setAtomic(AO);9576 XRead = cast<Value>(XLD);9577 } else if (XElemTy->isStructTy()) {9578 // FIXME: Add checks to ensure __atomic_load is emitted iff the9579 // target does not support `atomicrmw` of the size of the struct9580 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");9581 OldVal->setAtomic(AO);9582 const DataLayout &DL = OldVal->getModule()->getDataLayout();9583 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);9584 OpenMPIRBuilder::AtomicInfo atomicInfo(9585 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),9586 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);9587 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);9588 XRead = AtomicLoadRes.first;9589 OldVal->eraseFromParent();9590 } else {9591 // We need to perform atomic op as integer9592 IntegerType *IntCastTy =9593 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());9594 LoadInst *XLoad =9595 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");9596 XLoad->setAtomic(AO);9597 if (XElemTy->isFloatingPointTy()) {9598 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");9599 } else {9600 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");9601 }9602 }9603 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);9604 Builder.CreateStore(XRead, V.Var, V.IsVolatile);9605 return Builder.saveIP();9606}9607 9608OpenMPIRBuilder::InsertPointTy9609OpenMPIRBuilder::createAtomicWrite(const LocationDescription &Loc,9610 AtomicOpValue &X, Value *Expr,9611 AtomicOrdering AO, InsertPointTy AllocaIP) {9612 if (!updateToLocation(Loc))9613 return Loc.IP;9614 9615 assert(X.Var->getType()->isPointerTy() &&9616 "OMP Atomic expects a pointer to target memory");9617 Type *XElemTy = X.ElemTy;9618 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||9619 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&9620 "OMP atomic write expected a scalar type");9621 9622 if (XElemTy->isIntegerTy()) {9623 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);9624 XSt->setAtomic(AO);9625 } else if (XElemTy->isStructTy()) {9626 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");9627 const DataLayout &DL = OldVal->getModule()->getDataLayout();9628 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);9629 OpenMPIRBuilder::AtomicInfo atomicInfo(9630 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),9631 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);9632 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);9633 OldVal->eraseFromParent();9634 } else {9635 // We need to bitcast and perform atomic op as integers9636 IntegerType *IntCastTy =9637 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());9638 Value *ExprCast =9639 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");9640 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);9641 XSt->setAtomic(AO);9642 }9643 9644 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);9645 return Builder.saveIP();9646}9647 9648OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createAtomicUpdate(9649 const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X,9650 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,9651 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,9652 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {9653 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");9654 if (!updateToLocation(Loc))9655 return Loc.IP;9656 9657 LLVM_DEBUG({9658 Type *XTy = X.Var->getType();9659 assert(XTy->isPointerTy() &&9660 "OMP Atomic expects a pointer to target memory");9661 Type *XElemTy = X.ElemTy;9662 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||9663 XElemTy->isPointerTy()) &&9664 "OMP atomic update expected a scalar type");9665 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&9666 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&9667 "OpenMP atomic does not support LT or GT operations");9668 });9669 9670 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(9671 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,9672 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);9673 if (!AtomicResult)9674 return AtomicResult.takeError();9675 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);9676 return Builder.saveIP();9677}9678 9679// FIXME: Duplicating AtomicExpand9680Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,9681 AtomicRMWInst::BinOp RMWOp) {9682 switch (RMWOp) {9683 case AtomicRMWInst::Add:9684 return Builder.CreateAdd(Src1, Src2);9685 case AtomicRMWInst::Sub:9686 return Builder.CreateSub(Src1, Src2);9687 case AtomicRMWInst::And:9688 return Builder.CreateAnd(Src1, Src2);9689 case AtomicRMWInst::Nand:9690 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));9691 case AtomicRMWInst::Or:9692 return Builder.CreateOr(Src1, Src2);9693 case AtomicRMWInst::Xor:9694 return Builder.CreateXor(Src1, Src2);9695 case AtomicRMWInst::Xchg:9696 case AtomicRMWInst::FAdd:9697 case AtomicRMWInst::FSub:9698 case AtomicRMWInst::BAD_BINOP:9699 case AtomicRMWInst::Max:9700 case AtomicRMWInst::Min:9701 case AtomicRMWInst::UMax:9702 case AtomicRMWInst::UMin:9703 case AtomicRMWInst::FMax:9704 case AtomicRMWInst::FMin:9705 case AtomicRMWInst::FMaximum:9706 case AtomicRMWInst::FMinimum:9707 case AtomicRMWInst::UIncWrap:9708 case AtomicRMWInst::UDecWrap:9709 case AtomicRMWInst::USubCond:9710 case AtomicRMWInst::USubSat:9711 llvm_unreachable("Unsupported atomic update operation");9712 }9713 llvm_unreachable("Unsupported atomic update operation");9714}9715 9716Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(9717 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,9718 AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,9719 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,9720 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {9721 // TODO: handle the case where XElemTy is not byte-sized or not a power of 29722 // or a complex datatype.9723 bool emitRMWOp = false;9724 switch (RMWOp) {9725 case AtomicRMWInst::Add:9726 case AtomicRMWInst::And:9727 case AtomicRMWInst::Nand:9728 case AtomicRMWInst::Or:9729 case AtomicRMWInst::Xor:9730 case AtomicRMWInst::Xchg:9731 emitRMWOp = XElemTy;9732 break;9733 case AtomicRMWInst::Sub:9734 emitRMWOp = (IsXBinopExpr && XElemTy);9735 break;9736 default:9737 emitRMWOp = false;9738 }9739 emitRMWOp &= XElemTy->isIntegerTy();9740 9741 std::pair<Value *, Value *> Res;9742 if (emitRMWOp) {9743 AtomicRMWInst *RMWInst =9744 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);9745 if (T.isAMDGPU()) {9746 if (IsIgnoreDenormalMode)9747 RMWInst->setMetadata("amdgpu.ignore.denormal.mode",9748 llvm::MDNode::get(Builder.getContext(), {}));9749 if (!IsFineGrainedMemory)9750 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",9751 llvm::MDNode::get(Builder.getContext(), {}));9752 if (!IsRemoteMemory)9753 RMWInst->setMetadata("amdgpu.no.remote.memory",9754 llvm::MDNode::get(Builder.getContext(), {}));9755 }9756 Res.first = RMWInst;9757 // not needed except in case of postfix captures. Generate anyway for9758 // consistency with the else part. Will be removed with any DCE pass.9759 // AtomicRMWInst::Xchg does not have a coressponding instruction.9760 if (RMWOp == AtomicRMWInst::Xchg)9761 Res.second = Res.first;9762 else9763 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);9764 } else if (RMWOp == llvm::AtomicRMWInst::BinOp::BAD_BINOP &&9765 XElemTy->isStructTy()) {9766 LoadInst *OldVal =9767 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");9768 OldVal->setAtomic(AO);9769 const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();9770 unsigned LoadSize =9771 LoadDL.getTypeStoreSize(OldVal->getPointerOperand()->getType());9772 9773 OpenMPIRBuilder::AtomicInfo atomicInfo(9774 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),9775 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);9776 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);9777 BasicBlock *CurBB = Builder.GetInsertBlock();9778 Instruction *CurBBTI = CurBB->getTerminator();9779 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();9780 BasicBlock *ExitBB =9781 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");9782 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),9783 X->getName() + ".atomic.cont");9784 ContBB->getTerminator()->eraseFromParent();9785 Builder.restoreIP(AllocaIP);9786 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);9787 NewAtomicAddr->setName(X->getName() + "x.new.val");9788 Builder.SetInsertPoint(ContBB);9789 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);9790 PHI->addIncoming(AtomicLoadRes.first, CurBB);9791 Value *OldExprVal = PHI;9792 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);9793 if (!CBResult)9794 return CBResult.takeError();9795 Value *Upd = *CBResult;9796 Builder.CreateStore(Upd, NewAtomicAddr);9797 AtomicOrdering Failure =9798 llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);9799 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(9800 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);9801 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);9802 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());9803 Builder.CreateCondBr(Result.second, ExitBB, ContBB);9804 OldVal->eraseFromParent();9805 Res.first = OldExprVal;9806 Res.second = Upd;9807 9808 if (UnreachableInst *ExitTI =9809 dyn_cast<UnreachableInst>(ExitBB->getTerminator())) {9810 CurBBTI->eraseFromParent();9811 Builder.SetInsertPoint(ExitBB);9812 } else {9813 Builder.SetInsertPoint(ExitTI);9814 }9815 } else {9816 IntegerType *IntCastTy =9817 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());9818 LoadInst *OldVal =9819 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");9820 OldVal->setAtomic(AO);9821 // CurBB9822 // | /---\9823 // ContBB |9824 // | \---/9825 // ExitBB9826 BasicBlock *CurBB = Builder.GetInsertBlock();9827 Instruction *CurBBTI = CurBB->getTerminator();9828 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();9829 BasicBlock *ExitBB =9830 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");9831 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),9832 X->getName() + ".atomic.cont");9833 ContBB->getTerminator()->eraseFromParent();9834 Builder.restoreIP(AllocaIP);9835 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);9836 NewAtomicAddr->setName(X->getName() + "x.new.val");9837 Builder.SetInsertPoint(ContBB);9838 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);9839 PHI->addIncoming(OldVal, CurBB);9840 bool IsIntTy = XElemTy->isIntegerTy();9841 Value *OldExprVal = PHI;9842 if (!IsIntTy) {9843 if (XElemTy->isFloatingPointTy()) {9844 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,9845 X->getName() + ".atomic.fltCast");9846 } else {9847 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,9848 X->getName() + ".atomic.ptrCast");9849 }9850 }9851 9852 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);9853 if (!CBResult)9854 return CBResult.takeError();9855 Value *Upd = *CBResult;9856 Builder.CreateStore(Upd, NewAtomicAddr);9857 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);9858 AtomicOrdering Failure =9859 llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);9860 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(9861 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);9862 Result->setVolatile(VolatileX);9863 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);9864 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);9865 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());9866 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);9867 9868 Res.first = OldExprVal;9869 Res.second = Upd;9870 9871 // set Insertion point in exit block9872 if (UnreachableInst *ExitTI =9873 dyn_cast<UnreachableInst>(ExitBB->getTerminator())) {9874 CurBBTI->eraseFromParent();9875 Builder.SetInsertPoint(ExitBB);9876 } else {9877 Builder.SetInsertPoint(ExitTI);9878 }9879 }9880 9881 return Res;9882}9883 9884OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createAtomicCapture(9885 const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X,9886 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,9887 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,9888 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,9889 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {9890 if (!updateToLocation(Loc))9891 return Loc.IP;9892 9893 LLVM_DEBUG({9894 Type *XTy = X.Var->getType();9895 assert(XTy->isPointerTy() &&9896 "OMP Atomic expects a pointer to target memory");9897 Type *XElemTy = X.ElemTy;9898 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||9899 XElemTy->isPointerTy()) &&9900 "OMP atomic capture expected a scalar type");9901 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&9902 "OpenMP atomic does not support LT or GT operations");9903 });9904 9905 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',9906 // 'x' is simply atomically rewritten with 'expr'.9907 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);9908 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(9909 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,9910 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);9911 if (!AtomicResult)9912 return AtomicResult.takeError();9913 Value *CapturedVal =9914 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);9915 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);9916 9917 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);9918 return Builder.saveIP();9919}9920 9921OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createAtomicCompare(9922 const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V,9923 AtomicOpValue &R, Value *E, Value *D, AtomicOrdering AO,9924 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,9925 bool IsFailOnly) {9926 9927 AtomicOrdering Failure = AtomicCmpXchgInst::getStrongestFailureOrdering(AO);9928 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,9929 IsPostfixUpdate, IsFailOnly, Failure);9930}9931 9932OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createAtomicCompare(9933 const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V,9934 AtomicOpValue &R, Value *E, Value *D, AtomicOrdering AO,9935 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,9936 bool IsFailOnly, AtomicOrdering Failure) {9937 9938 if (!updateToLocation(Loc))9939 return Loc.IP;9940 9941 assert(X.Var->getType()->isPointerTy() &&9942 "OMP atomic expects a pointer to target memory");9943 // compare capture9944 if (V.Var) {9945 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");9946 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");9947 }9948 9949 bool IsInteger = E->getType()->isIntegerTy();9950 9951 if (Op == OMPAtomicCompareOp::EQ) {9952 AtomicCmpXchgInst *Result = nullptr;9953 if (!IsInteger) {9954 IntegerType *IntCastTy =9955 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());9956 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);9957 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);9958 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast, MaybeAlign(),9959 AO, Failure);9960 } else {9961 Result =9962 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);9963 }9964 9965 if (V.Var) {9966 Value *OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);9967 if (!IsInteger)9968 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);9969 assert(OldValue->getType() == V.ElemTy &&9970 "OldValue and V must be of same type");9971 if (IsPostfixUpdate) {9972 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);9973 } else {9974 Value *SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);9975 if (IsFailOnly) {9976 // CurBB----9977 // | |9978 // v |9979 // ContBB |9980 // | |9981 // v |9982 // ExitBB <-9983 //9984 // where ContBB only contains the store of old value to 'v'.9985 BasicBlock *CurBB = Builder.GetInsertBlock();9986 Instruction *CurBBTI = CurBB->getTerminator();9987 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();9988 BasicBlock *ExitBB = CurBB->splitBasicBlock(9989 CurBBTI, X.Var->getName() + ".atomic.exit");9990 BasicBlock *ContBB = CurBB->splitBasicBlock(9991 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");9992 ContBB->getTerminator()->eraseFromParent();9993 CurBB->getTerminator()->eraseFromParent();9994 9995 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);9996 9997 Builder.SetInsertPoint(ContBB);9998 Builder.CreateStore(OldValue, V.Var);9999 Builder.CreateBr(ExitBB);10000 10001 if (UnreachableInst *ExitTI =10002 dyn_cast<UnreachableInst>(ExitBB->getTerminator())) {10003 CurBBTI->eraseFromParent();10004 Builder.SetInsertPoint(ExitBB);10005 } else {10006 Builder.SetInsertPoint(ExitTI);10007 }10008 } else {10009 Value *CapturedValue =10010 Builder.CreateSelect(SuccessOrFail, E, OldValue);10011 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);10012 }10013 }10014 }10015 // The comparison result has to be stored.10016 if (R.Var) {10017 assert(R.Var->getType()->isPointerTy() &&10018 "r.var must be of pointer type");10019 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");10020 10021 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);10022 Value *ResultCast = R.IsSigned10023 ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)10024 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);10025 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);10026 }10027 } else {10028 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&10029 "Op should be either max or min at this point");10030 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");10031 10032 // Reverse the ordop as the OpenMP forms are different from LLVM forms.10033 // Let's take max as example.10034 // OpenMP form:10035 // x = x > expr ? expr : x;10036 // LLVM form:10037 // *ptr = *ptr > val ? *ptr : val;10038 // We need to transform to LLVM form.10039 // x = x <= expr ? x : expr;10040 AtomicRMWInst::BinOp NewOp;10041 if (IsXBinopExpr) {10042 if (IsInteger) {10043 if (X.IsSigned)10044 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min10045 : AtomicRMWInst::Max;10046 else10047 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin10048 : AtomicRMWInst::UMax;10049 } else {10050 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin10051 : AtomicRMWInst::FMax;10052 }10053 } else {10054 if (IsInteger) {10055 if (X.IsSigned)10056 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max10057 : AtomicRMWInst::Min;10058 else10059 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax10060 : AtomicRMWInst::UMin;10061 } else {10062 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax10063 : AtomicRMWInst::FMin;10064 }10065 }10066 10067 AtomicRMWInst *OldValue =10068 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);10069 if (V.Var) {10070 Value *CapturedValue = nullptr;10071 if (IsPostfixUpdate) {10072 CapturedValue = OldValue;10073 } else {10074 CmpInst::Predicate Pred;10075 switch (NewOp) {10076 case AtomicRMWInst::Max:10077 Pred = CmpInst::ICMP_SGT;10078 break;10079 case AtomicRMWInst::UMax:10080 Pred = CmpInst::ICMP_UGT;10081 break;10082 case AtomicRMWInst::FMax:10083 Pred = CmpInst::FCMP_OGT;10084 break;10085 case AtomicRMWInst::Min:10086 Pred = CmpInst::ICMP_SLT;10087 break;10088 case AtomicRMWInst::UMin:10089 Pred = CmpInst::ICMP_ULT;10090 break;10091 case AtomicRMWInst::FMin:10092 Pred = CmpInst::FCMP_OLT;10093 break;10094 default:10095 llvm_unreachable("unexpected comparison op");10096 }10097 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);10098 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);10099 }10100 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);10101 }10102 }10103 10104 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);10105 10106 return Builder.saveIP();10107}10108 10109OpenMPIRBuilder::InsertPointOrErrorTy10110OpenMPIRBuilder::createTeams(const LocationDescription &Loc,10111 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,10112 Value *NumTeamsUpper, Value *ThreadLimit,10113 Value *IfExpr) {10114 if (!updateToLocation(Loc))10115 return InsertPointTy();10116 10117 uint32_t SrcLocStrSize;10118 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);10119 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);10120 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();10121 10122 // Outer allocation basicblock is the entry block of the current function.10123 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();10124 if (&OuterAllocaBB == Builder.GetInsertBlock()) {10125 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");10126 Builder.SetInsertPoint(BodyBB, BodyBB->begin());10127 }10128 10129 // The current basic block is split into four basic blocks. After outlining,10130 // they will be mapped as follows:10131 // ```10132 // def current_fn() {10133 // current_basic_block:10134 // br label %teams.exit10135 // teams.exit:10136 // ; instructions after teams10137 // }10138 //10139 // def outlined_fn() {10140 // teams.alloca:10141 // br label %teams.body10142 // teams.body:10143 // ; instructions within teams body10144 // }10145 // ```10146 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");10147 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");10148 BasicBlock *AllocaBB =10149 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");10150 10151 bool SubClausesPresent =10152 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);10153 // Push num_teams10154 if (!Config.isTargetDevice() && SubClausesPresent) {10155 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&10156 "if lowerbound is non-null, then upperbound must also be non-null "10157 "for bounds on num_teams");10158 10159 if (NumTeamsUpper == nullptr)10160 NumTeamsUpper = Builder.getInt32(0);10161 10162 if (NumTeamsLower == nullptr)10163 NumTeamsLower = NumTeamsUpper;10164 10165 if (IfExpr) {10166 assert(IfExpr->getType()->isIntegerTy() &&10167 "argument to if clause must be an integer value");10168 10169 // upper = ifexpr ? upper : 110170 if (IfExpr->getType() != Int1)10171 IfExpr = Builder.CreateICmpNE(IfExpr,10172 ConstantInt::get(IfExpr->getType(), 0));10173 NumTeamsUpper = Builder.CreateSelect(10174 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");10175 10176 // lower = ifexpr ? lower : 110177 NumTeamsLower = Builder.CreateSelect(10178 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");10179 }10180 10181 if (ThreadLimit == nullptr)10182 ThreadLimit = Builder.getInt32(0);10183 10184 Value *ThreadNum = getOrCreateThreadID(Ident);10185 createRuntimeFunctionCall(10186 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),10187 {Ident, ThreadNum, NumTeamsLower, NumTeamsUpper, ThreadLimit});10188 }10189 // Generate the body of teams.10190 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());10191 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());10192 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP))10193 return Err;10194 10195 OutlineInfo OI;10196 OI.EntryBB = AllocaBB;10197 OI.ExitBB = ExitBB;10198 OI.OuterAllocaBB = &OuterAllocaBB;10199 10200 // Insert fake values for global tid and bound tid.10201 SmallVector<Instruction *, 8> ToBeDeleted;10202 InsertPointTy OuterAllocaIP(&OuterAllocaBB, OuterAllocaBB.begin());10203 OI.ExcludeArgsFromAggregate.push_back(createFakeIntVal(10204 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));10205 OI.ExcludeArgsFromAggregate.push_back(createFakeIntVal(10206 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));10207 10208 auto HostPostOutlineCB = [this, Ident,10209 ToBeDeleted](Function &OutlinedFn) mutable {10210 // The stale call instruction will be replaced with a new call instruction10211 // for runtime call with the outlined function.10212 10213 assert(OutlinedFn.hasOneUse() &&10214 "there must be a single user for the outlined function");10215 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());10216 ToBeDeleted.push_back(StaleCI);10217 10218 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&10219 "Outlined function must have two or three arguments only");10220 10221 bool HasShared = OutlinedFn.arg_size() == 3;10222 10223 OutlinedFn.getArg(0)->setName("global.tid.ptr");10224 OutlinedFn.getArg(1)->setName("bound.tid.ptr");10225 if (HasShared)10226 OutlinedFn.getArg(2)->setName("data");10227 10228 // Call to the runtime function for teams in the current function.10229 assert(StaleCI && "Error while outlining - no CallInst user found for the "10230 "outlined function.");10231 Builder.SetInsertPoint(StaleCI);10232 SmallVector<Value *> Args = {10233 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};10234 if (HasShared)10235 Args.push_back(StaleCI->getArgOperand(2));10236 createRuntimeFunctionCall(10237 getOrCreateRuntimeFunctionPtr(10238 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),10239 Args);10240 10241 for (Instruction *I : llvm::reverse(ToBeDeleted))10242 I->eraseFromParent();10243 };10244 10245 if (!Config.isTargetDevice())10246 OI.PostOutlineCB = HostPostOutlineCB;10247 10248 addOutlineInfo(std::move(OI));10249 10250 Builder.SetInsertPoint(ExitBB, ExitBB->begin());10251 10252 return Builder.saveIP();10253}10254 10255OpenMPIRBuilder::InsertPointOrErrorTy10256OpenMPIRBuilder::createDistribute(const LocationDescription &Loc,10257 InsertPointTy OuterAllocaIP,10258 BodyGenCallbackTy BodyGenCB) {10259 if (!updateToLocation(Loc))10260 return InsertPointTy();10261 10262 BasicBlock *OuterAllocaBB = OuterAllocaIP.getBlock();10263 10264 if (OuterAllocaBB == Builder.GetInsertBlock()) {10265 BasicBlock *BodyBB =10266 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");10267 Builder.SetInsertPoint(BodyBB, BodyBB->begin());10268 }10269 BasicBlock *ExitBB =10270 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");10271 BasicBlock *BodyBB =10272 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");10273 BasicBlock *AllocaBB =10274 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");10275 10276 // Generate the body of distribute clause10277 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());10278 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());10279 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP))10280 return Err;10281 10282 // When using target we use different runtime functions which require a10283 // callback.10284 if (Config.isTargetDevice()) {10285 OutlineInfo OI;10286 OI.OuterAllocaBB = OuterAllocaIP.getBlock();10287 OI.EntryBB = AllocaBB;10288 OI.ExitBB = ExitBB;10289 10290 addOutlineInfo(std::move(OI));10291 }10292 Builder.SetInsertPoint(ExitBB, ExitBB->begin());10293 10294 return Builder.saveIP();10295}10296 10297GlobalVariable *10298OpenMPIRBuilder::createOffloadMapnames(SmallVectorImpl<llvm::Constant *> &Names,10299 std::string VarName) {10300 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(10301 llvm::ArrayType::get(llvm::PointerType::getUnqual(M.getContext()),10302 Names.size()),10303 Names);10304 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(10305 M, MapNamesArrayInit->getType(),10306 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,10307 VarName);10308 return MapNamesArrayGlobal;10309}10310 10311// Create all simple and struct types exposed by the runtime and remember10312// the llvm::PointerTypes of them for easy access later.10313void OpenMPIRBuilder::initializeTypes(Module &M) {10314 LLVMContext &Ctx = M.getContext();10315 StructType *T;10316 unsigned DefaultTargetAS = Config.getDefaultTargetAS();10317 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();10318#define OMP_TYPE(VarName, InitValue) VarName = InitValue;10319#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \10320 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \10321 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);10322#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \10323 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \10324 VarName##Ptr = PointerType::get(Ctx, ProgramAS);10325#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \10326 T = StructType::getTypeByName(Ctx, StructName); \10327 if (!T) \10328 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \10329 VarName = T; \10330 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);10331#include "llvm/Frontend/OpenMP/OMPKinds.def"10332}10333 10334void OpenMPIRBuilder::OutlineInfo::collectBlocks(10335 SmallPtrSetImpl<BasicBlock *> &BlockSet,10336 SmallVectorImpl<BasicBlock *> &BlockVector) {10337 SmallVector<BasicBlock *, 32> Worklist;10338 BlockSet.insert(EntryBB);10339 BlockSet.insert(ExitBB);10340 10341 Worklist.push_back(EntryBB);10342 while (!Worklist.empty()) {10343 BasicBlock *BB = Worklist.pop_back_val();10344 BlockVector.push_back(BB);10345 for (BasicBlock *SuccBB : successors(BB))10346 if (BlockSet.insert(SuccBB).second)10347 Worklist.push_back(SuccBB);10348 }10349}10350 10351void OpenMPIRBuilder::createOffloadEntry(Constant *ID, Constant *Addr,10352 uint64_t Size, int32_t Flags,10353 GlobalValue::LinkageTypes,10354 StringRef Name) {10355 if (!Config.isGPU()) {10356 llvm::offloading::emitOffloadingEntry(10357 M, object::OffloadKind::OFK_OpenMP, ID,10358 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);10359 return;10360 }10361 // TODO: Add support for global variables on the device after declare target10362 // support.10363 Function *Fn = dyn_cast<Function>(Addr);10364 if (!Fn)10365 return;10366 10367 // Add a function attribute for the kernel.10368 Fn->addFnAttr("kernel");10369 if (T.isAMDGCN())10370 Fn->addFnAttr("uniform-work-group-size", "true");10371 Fn->addFnAttr(Attribute::MustProgress);10372}10373 10374// We only generate metadata for function that contain target regions.10375void OpenMPIRBuilder::createOffloadEntriesAndInfoMetadata(10376 EmitMetadataErrorReportFunctionTy &ErrorFn) {10377 10378 // If there are no entries, we don't need to do anything.10379 if (OffloadInfoManager.empty())10380 return;10381 10382 LLVMContext &C = M.getContext();10383 SmallVector<std::pair<const OffloadEntriesInfoManager::OffloadEntryInfo *,10384 TargetRegionEntryInfo>,10385 16>10386 OrderedEntries(OffloadInfoManager.size());10387 10388 // Auxiliary methods to create metadata values and strings.10389 auto &&GetMDInt = [this](unsigned V) {10390 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));10391 };10392 10393 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };10394 10395 // Create the offloading info metadata node.10396 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");10397 auto &&TargetRegionMetadataEmitter =10398 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](10399 const TargetRegionEntryInfo &EntryInfo,10400 const OffloadEntriesInfoManager::OffloadEntryInfoTargetRegion &E) {10401 // Generate metadata for target regions. Each entry of this metadata10402 // contains:10403 // - Entry 0 -> Kind of this type of metadata (0).10404 // - Entry 1 -> Device ID of the file where the entry was identified.10405 // - Entry 2 -> File ID of the file where the entry was identified.10406 // - Entry 3 -> Mangled name of the function where the entry was10407 // identified.10408 // - Entry 4 -> Line in the file where the entry was identified.10409 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.10410 // - Entry 6 -> Order the entry was created.10411 // The first element of the metadata node is the kind.10412 Metadata *Ops[] = {10413 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),10414 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),10415 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),10416 GetMDInt(E.getOrder())};10417 10418 // Save this entry in the right position of the ordered entries array.10419 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);10420 10421 // Add metadata to the named metadata node.10422 MD->addOperand(MDNode::get(C, Ops));10423 };10424 10425 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);10426 10427 // Create function that emits metadata for each device global variable entry;10428 auto &&DeviceGlobalVarMetadataEmitter =10429 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](10430 StringRef MangledName,10431 const OffloadEntriesInfoManager::OffloadEntryInfoDeviceGlobalVar &E) {10432 // Generate metadata for global variables. Each entry of this metadata10433 // contains:10434 // - Entry 0 -> Kind of this type of metadata (1).10435 // - Entry 1 -> Mangled name of the variable.10436 // - Entry 2 -> Declare target kind.10437 // - Entry 3 -> Order the entry was created.10438 // The first element of the metadata node is the kind.10439 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),10440 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};10441 10442 // Save this entry in the right position of the ordered entries array.10443 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);10444 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);10445 10446 // Add metadata to the named metadata node.10447 MD->addOperand(MDNode::get(C, Ops));10448 };10449 10450 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(10451 DeviceGlobalVarMetadataEmitter);10452 10453 for (const auto &E : OrderedEntries) {10454 assert(E.first && "All ordered entries must exist!");10455 if (const auto *CE =10456 dyn_cast<OffloadEntriesInfoManager::OffloadEntryInfoTargetRegion>(10457 E.first)) {10458 if (!CE->getID() || !CE->getAddress()) {10459 // Do not blame the entry if the parent funtion is not emitted.10460 TargetRegionEntryInfo EntryInfo = E.second;10461 StringRef FnName = EntryInfo.ParentName;10462 if (!M.getNamedValue(FnName))10463 continue;10464 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);10465 continue;10466 }10467 createOffloadEntry(CE->getID(), CE->getAddress(),10468 /*Size=*/0, CE->getFlags(),10469 GlobalValue::WeakAnyLinkage);10470 } else if (const auto *CE = dyn_cast<10471 OffloadEntriesInfoManager::OffloadEntryInfoDeviceGlobalVar>(10472 E.first)) {10473 OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind Flags =10474 static_cast<OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind>(10475 CE->getFlags());10476 switch (Flags) {10477 case OffloadEntriesInfoManager::OMPTargetGlobalVarEntryEnter:10478 case OffloadEntriesInfoManager::OMPTargetGlobalVarEntryTo:10479 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())10480 continue;10481 if (!CE->getAddress()) {10482 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);10483 continue;10484 }10485 // The vaiable has no definition - no need to add the entry.10486 if (CE->getVarSize() == 0)10487 continue;10488 break;10489 case OffloadEntriesInfoManager::OMPTargetGlobalVarEntryLink:10490 assert(((Config.isTargetDevice() && !CE->getAddress()) ||10491 (!Config.isTargetDevice() && CE->getAddress())) &&10492 "Declaret target link address is set.");10493 if (Config.isTargetDevice())10494 continue;10495 if (!CE->getAddress()) {10496 ErrorFn(EMIT_MD_GLOBAL_VAR_LINK_ERROR, TargetRegionEntryInfo());10497 continue;10498 }10499 break;10500 default:10501 break;10502 }10503 10504 // Hidden or internal symbols on the device are not externally visible.10505 // We should not attempt to register them by creating an offloading10506 // entry. Indirect variables are handled separately on the device.10507 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))10508 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&10509 Flags != OffloadEntriesInfoManager::OMPTargetGlobalVarEntryIndirect)10510 continue;10511 10512 // Indirect globals need to use a special name that doesn't match the name10513 // of the associated host global.10514 if (Flags == OffloadEntriesInfoManager::OMPTargetGlobalVarEntryIndirect)10515 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),10516 Flags, CE->getLinkage(), CE->getVarName());10517 else10518 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),10519 Flags, CE->getLinkage());10520 10521 } else {10522 llvm_unreachable("Unsupported entry kind.");10523 }10524 }10525 10526 // Emit requires directive globals to a special entry so the runtime can10527 // register them when the device image is loaded.10528 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading10529 // entries should be redesigned to better suit this use-case.10530 if (Config.hasRequiresFlags() && !Config.isTargetDevice())10531 offloading::emitOffloadingEntry(10532 M, object::OffloadKind::OFK_OpenMP,10533 Constant::getNullValue(PointerType::getUnqual(M.getContext())),10534 ".requires", /*Size=*/0,10535 OffloadEntriesInfoManager::OMPTargetGlobalRegisterRequires,10536 Config.getRequiresFlags());10537}10538 10539void TargetRegionEntryInfo::getTargetRegionEntryFnName(10540 SmallVectorImpl<char> &Name, StringRef ParentName, unsigned DeviceID,10541 unsigned FileID, unsigned Line, unsigned Count) {10542 raw_svector_ostream OS(Name);10543 OS << KernelNamePrefix << llvm::format("%x", DeviceID)10544 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;10545 if (Count)10546 OS << "_" << Count;10547}10548 10549void OffloadEntriesInfoManager::getTargetRegionEntryFnName(10550 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {10551 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);10552 TargetRegionEntryInfo::getTargetRegionEntryFnName(10553 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,10554 EntryInfo.Line, NewCount);10555}10556 10557TargetRegionEntryInfo10558OpenMPIRBuilder::getTargetEntryUniqueInfo(FileIdentifierInfoCallbackTy CallBack,10559 vfs::FileSystem &VFS,10560 StringRef ParentName) {10561 sys::fs::UniqueID ID(0xdeadf17e, 0);10562 auto FileIDInfo = CallBack();10563 uint64_t FileID = 0;10564 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {10565 ID = Status->getUniqueID();10566 FileID = Status->getUniqueID().getFile();10567 } else {10568 // If the inode ID could not be determined, create a hash value10569 // the current file name and use that as an ID.10570 FileID = hash_value(std::get<0>(FileIDInfo));10571 }10572 10573 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,10574 std::get<1>(FileIDInfo));10575}10576 10577unsigned OpenMPIRBuilder::getFlagMemberOffset() {10578 unsigned Offset = 0;10579 for (uint64_t Remain =10580 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(10581 omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);10582 !(Remain & 1); Remain = Remain >> 1)10583 Offset++;10584 return Offset;10585}10586 10587omp::OpenMPOffloadMappingFlags10588OpenMPIRBuilder::getMemberOfFlag(unsigned Position) {10589 // Rotate by getFlagMemberOffset() bits.10590 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)10591 << getFlagMemberOffset());10592}10593 10594void OpenMPIRBuilder::setCorrectMemberOfFlag(10595 omp::OpenMPOffloadMappingFlags &Flags,10596 omp::OpenMPOffloadMappingFlags MemberOfFlag) {10597 // If the entry is PTR_AND_OBJ but has not been marked with the special10598 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be10599 // marked as MEMBER_OF.10600 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(10601 Flags & omp::OpenMPOffloadMappingFlags::OMP_MAP_PTR_AND_OBJ) &&10602 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(10603 (Flags & omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF) !=10604 omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF))10605 return;10606 10607 // Reset the placeholder value to prepare the flag for the assignment of the10608 // proper MEMBER_OF value.10609 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;10610 Flags |= MemberOfFlag;10611}10612 10613Constant *OpenMPIRBuilder::getAddrOfDeclareTargetVar(10614 OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause,10615 OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause,10616 bool IsDeclaration, bool IsExternallyVisible,10617 TargetRegionEntryInfo EntryInfo, StringRef MangledName,10618 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,10619 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,10620 std::function<Constant *()> GlobalInitializer,10621 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {10622 // TODO: convert this to utilise the IRBuilder Config rather than10623 // a passed down argument.10624 if (OpenMPSIMD)10625 return nullptr;10626 10627 if (CaptureClause == OffloadEntriesInfoManager::OMPTargetGlobalVarEntryLink ||10628 ((CaptureClause == OffloadEntriesInfoManager::OMPTargetGlobalVarEntryTo ||10629 CaptureClause ==10630 OffloadEntriesInfoManager::OMPTargetGlobalVarEntryEnter) &&10631 Config.hasRequiresUnifiedSharedMemory())) {10632 SmallString<64> PtrName;10633 {10634 raw_svector_ostream OS(PtrName);10635 OS << MangledName;10636 if (!IsExternallyVisible)10637 OS << format("_%x", EntryInfo.FileID);10638 OS << "_decl_tgt_ref_ptr";10639 }10640 10641 Value *Ptr = M.getNamedValue(PtrName);10642 10643 if (!Ptr) {10644 GlobalValue *GlobalValue = M.getNamedValue(MangledName);10645 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);10646 10647 auto *GV = cast<GlobalVariable>(Ptr);10648 GV->setLinkage(GlobalValue::WeakAnyLinkage);10649 10650 if (!Config.isTargetDevice()) {10651 if (GlobalInitializer)10652 GV->setInitializer(GlobalInitializer());10653 else10654 GV->setInitializer(GlobalValue);10655 }10656 10657 registerTargetGlobalVariable(10658 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,10659 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,10660 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));10661 }10662 10663 return cast<Constant>(Ptr);10664 }10665 10666 return nullptr;10667}10668 10669void OpenMPIRBuilder::registerTargetGlobalVariable(10670 OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause,10671 OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause,10672 bool IsDeclaration, bool IsExternallyVisible,10673 TargetRegionEntryInfo EntryInfo, StringRef MangledName,10674 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,10675 std::vector<Triple> TargetTriple,10676 std::function<Constant *()> GlobalInitializer,10677 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,10678 Constant *Addr) {10679 if (DeviceClause != OffloadEntriesInfoManager::OMPTargetDeviceClauseAny ||10680 (TargetTriple.empty() && !Config.isTargetDevice()))10681 return;10682 10683 OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind Flags;10684 StringRef VarName;10685 int64_t VarSize;10686 GlobalValue::LinkageTypes Linkage;10687 10688 if ((CaptureClause == OffloadEntriesInfoManager::OMPTargetGlobalVarEntryTo ||10689 CaptureClause ==10690 OffloadEntriesInfoManager::OMPTargetGlobalVarEntryEnter) &&10691 !Config.hasRequiresUnifiedSharedMemory()) {10692 Flags = OffloadEntriesInfoManager::OMPTargetGlobalVarEntryTo;10693 VarName = MangledName;10694 GlobalValue *LlvmVal = M.getNamedValue(VarName);10695 10696 if (!IsDeclaration)10697 VarSize = divideCeil(10698 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);10699 else10700 VarSize = 0;10701 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();10702 10703 // This is a workaround carried over from Clang which prevents undesired10704 // optimisation of internal variables.10705 if (Config.isTargetDevice() &&10706 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {10707 // Do not create a "ref-variable" if the original is not also available10708 // on the host.10709 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))10710 return;10711 10712 std::string RefName = createPlatformSpecificName({VarName, "ref"});10713 10714 if (!M.getNamedValue(RefName)) {10715 Constant *AddrRef =10716 getOrCreateInternalVariable(Addr->getType(), RefName);10717 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);10718 GvAddrRef->setConstant(true);10719 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);10720 GvAddrRef->setInitializer(Addr);10721 GeneratedRefs.push_back(GvAddrRef);10722 }10723 }10724 } else {10725 if (CaptureClause == OffloadEntriesInfoManager::OMPTargetGlobalVarEntryLink)10726 Flags = OffloadEntriesInfoManager::OMPTargetGlobalVarEntryLink;10727 else10728 Flags = OffloadEntriesInfoManager::OMPTargetGlobalVarEntryTo;10729 10730 if (Config.isTargetDevice()) {10731 VarName = (Addr) ? Addr->getName() : "";10732 Addr = nullptr;10733 } else {10734 Addr = getAddrOfDeclareTargetVar(10735 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,10736 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,10737 LlvmPtrTy, GlobalInitializer, VariableLinkage);10738 VarName = (Addr) ? Addr->getName() : "";10739 }10740 VarSize = M.getDataLayout().getPointerSize();10741 Linkage = GlobalValue::WeakAnyLinkage;10742 }10743 10744 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,10745 Flags, Linkage);10746}10747 10748/// Loads all the offload entries information from the host IR10749/// metadata.10750void OpenMPIRBuilder::loadOffloadInfoMetadata(Module &M) {10751 // If we are in target mode, load the metadata from the host IR. This code has10752 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().10753 10754 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);10755 if (!MD)10756 return;10757 10758 for (MDNode *MN : MD->operands()) {10759 auto &&GetMDInt = [MN](unsigned Idx) {10760 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));10761 return cast<ConstantInt>(V->getValue())->getZExtValue();10762 };10763 10764 auto &&GetMDString = [MN](unsigned Idx) {10765 auto *V = cast<MDString>(MN->getOperand(Idx));10766 return V->getString();10767 };10768 10769 switch (GetMDInt(0)) {10770 default:10771 llvm_unreachable("Unexpected metadata!");10772 break;10773 case OffloadEntriesInfoManager::OffloadEntryInfo::10774 OffloadingEntryInfoTargetRegion: {10775 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),10776 /*DeviceID=*/GetMDInt(1),10777 /*FileID=*/GetMDInt(2),10778 /*Line=*/GetMDInt(4),10779 /*Count=*/GetMDInt(5));10780 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,10781 /*Order=*/GetMDInt(6));10782 break;10783 }10784 case OffloadEntriesInfoManager::OffloadEntryInfo::10785 OffloadingEntryInfoDeviceGlobalVar:10786 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(10787 /*MangledName=*/GetMDString(1),10788 static_cast<OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind>(10789 /*Flags=*/GetMDInt(2)),10790 /*Order=*/GetMDInt(3));10791 break;10792 }10793 }10794}10795 10796void OpenMPIRBuilder::loadOffloadInfoMetadata(vfs::FileSystem &VFS,10797 StringRef HostFilePath) {10798 if (HostFilePath.empty())10799 return;10800 10801 auto Buf = VFS.getBufferForFile(HostFilePath);10802 if (std::error_code Err = Buf.getError()) {10803 report_fatal_error(("error opening host file from host file path inside of "10804 "OpenMPIRBuilder: " +10805 Err.message())10806 .c_str());10807 }10808 10809 LLVMContext Ctx;10810 auto M = expectedToErrorOrAndEmitErrors(10811 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));10812 if (std::error_code Err = M.getError()) {10813 report_fatal_error(10814 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())10815 .c_str());10816 }10817 10818 loadOffloadInfoMetadata(*M.get());10819}10820 10821//===----------------------------------------------------------------------===//10822// OffloadEntriesInfoManager10823//===----------------------------------------------------------------------===//10824 10825bool OffloadEntriesInfoManager::empty() const {10826 return OffloadEntriesTargetRegion.empty() &&10827 OffloadEntriesDeviceGlobalVar.empty();10828}10829 10830unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(10831 const TargetRegionEntryInfo &EntryInfo) const {10832 auto It = OffloadEntriesTargetRegionCount.find(10833 getTargetRegionEntryCountKey(EntryInfo));10834 if (It == OffloadEntriesTargetRegionCount.end())10835 return 0;10836 return It->second;10837}10838 10839void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(10840 const TargetRegionEntryInfo &EntryInfo) {10841 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =10842 EntryInfo.Count + 1;10843}10844 10845/// Initialize target region entry.10846void OffloadEntriesInfoManager::initializeTargetRegionEntryInfo(10847 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {10848 OffloadEntriesTargetRegion[EntryInfo] =10849 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,10850 OMPTargetRegionEntryTargetRegion);10851 ++OffloadingEntriesNum;10852}10853 10854void OffloadEntriesInfoManager::registerTargetRegionEntryInfo(10855 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,10856 OMPTargetRegionEntryKind Flags) {10857 assert(EntryInfo.Count == 0 && "expected default EntryInfo");10858 10859 // Update the EntryInfo with the next available count for this location.10860 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);10861 10862 // If we are emitting code for a target, the entry is already initialized,10863 // only has to be registered.10864 if (OMPBuilder->Config.isTargetDevice()) {10865 // This could happen if the device compilation is invoked standalone.10866 if (!hasTargetRegionEntryInfo(EntryInfo)) {10867 return;10868 }10869 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];10870 Entry.setAddress(Addr);10871 Entry.setID(ID);10872 Entry.setFlags(Flags);10873 } else {10874 if (Flags == OffloadEntriesInfoManager::OMPTargetRegionEntryTargetRegion &&10875 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))10876 return;10877 assert(!hasTargetRegionEntryInfo(EntryInfo) &&10878 "Target region entry already registered!");10879 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);10880 OffloadEntriesTargetRegion[EntryInfo] = Entry;10881 ++OffloadingEntriesNum;10882 }10883 incrementTargetRegionEntryInfoCount(EntryInfo);10884}10885 10886bool OffloadEntriesInfoManager::hasTargetRegionEntryInfo(10887 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {10888 10889 // Update the EntryInfo with the next available count for this location.10890 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);10891 10892 auto It = OffloadEntriesTargetRegion.find(EntryInfo);10893 if (It == OffloadEntriesTargetRegion.end()) {10894 return false;10895 }10896 // Fail if this entry is already registered.10897 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))10898 return false;10899 return true;10900}10901 10902void OffloadEntriesInfoManager::actOnTargetRegionEntriesInfo(10903 const OffloadTargetRegionEntryInfoActTy &Action) {10904 // Scan all target region entries and perform the provided action.10905 for (const auto &It : OffloadEntriesTargetRegion) {10906 Action(It.first, It.second);10907 }10908}10909 10910void OffloadEntriesInfoManager::initializeDeviceGlobalVarEntryInfo(10911 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {10912 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);10913 ++OffloadingEntriesNum;10914}10915 10916void OffloadEntriesInfoManager::registerDeviceGlobalVarEntryInfo(10917 StringRef VarName, Constant *Addr, int64_t VarSize,10918 OMPTargetGlobalVarEntryKind Flags, GlobalValue::LinkageTypes Linkage) {10919 if (OMPBuilder->Config.isTargetDevice()) {10920 // This could happen if the device compilation is invoked standalone.10921 if (!hasDeviceGlobalVarEntryInfo(VarName))10922 return;10923 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];10924 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {10925 if (Entry.getVarSize() == 0) {10926 Entry.setVarSize(VarSize);10927 Entry.setLinkage(Linkage);10928 }10929 return;10930 }10931 Entry.setVarSize(VarSize);10932 Entry.setLinkage(Linkage);10933 Entry.setAddress(Addr);10934 } else {10935 if (hasDeviceGlobalVarEntryInfo(VarName)) {10936 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];10937 assert(Entry.isValid() && Entry.getFlags() == Flags &&10938 "Entry not initialized!");10939 if (Entry.getVarSize() == 0) {10940 Entry.setVarSize(VarSize);10941 Entry.setLinkage(Linkage);10942 }10943 return;10944 }10945 if (Flags == OffloadEntriesInfoManager::OMPTargetGlobalVarEntryIndirect)10946 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,10947 Addr, VarSize, Flags, Linkage,10948 VarName.str());10949 else10950 OffloadEntriesDeviceGlobalVar.try_emplace(10951 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");10952 ++OffloadingEntriesNum;10953 }10954}10955 10956void OffloadEntriesInfoManager::actOnDeviceGlobalVarEntriesInfo(10957 const OffloadDeviceGlobalVarEntryInfoActTy &Action) {10958 // Scan all target region entries and perform the provided action.10959 for (const auto &E : OffloadEntriesDeviceGlobalVar)10960 Action(E.getKey(), E.getValue());10961}10962 10963//===----------------------------------------------------------------------===//10964// CanonicalLoopInfo10965//===----------------------------------------------------------------------===//10966 10967void CanonicalLoopInfo::collectControlBlocks(10968 SmallVectorImpl<BasicBlock *> &BBs) {10969 // We only count those BBs as control block for which we do not need to10970 // reverse the CFG, i.e. not the loop body which can contain arbitrary control10971 // flow. For consistency, this also means we do not add the Body block, which10972 // is just the entry to the body code.10973 BBs.reserve(BBs.size() + 6);10974 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});10975}10976 10977BasicBlock *CanonicalLoopInfo::getPreheader() const {10978 assert(isValid() && "Requires a valid canonical loop");10979 for (BasicBlock *Pred : predecessors(Header)) {10980 if (Pred != Latch)10981 return Pred;10982 }10983 llvm_unreachable("Missing preheader");10984}10985 10986void CanonicalLoopInfo::setTripCount(Value *TripCount) {10987 assert(isValid() && "Requires a valid canonical loop");10988 10989 Instruction *CmpI = &getCond()->front();10990 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");10991 CmpI->setOperand(1, TripCount);10992 10993#ifndef NDEBUG10994 assertOK();10995#endif10996}10997 10998void CanonicalLoopInfo::mapIndVar(10999 llvm::function_ref<Value *(Instruction *)> Updater) {11000 assert(isValid() && "Requires a valid canonical loop");11001 11002 Instruction *OldIV = getIndVar();11003 11004 // Record all uses excluding those introduced by the updater. Uses by the11005 // CanonicalLoopInfo itself to keep track of the number of iterations are11006 // excluded.11007 SmallVector<Use *> ReplacableUses;11008 for (Use &U : OldIV->uses()) {11009 auto *User = dyn_cast<Instruction>(U.getUser());11010 if (!User)11011 continue;11012 if (User->getParent() == getCond())11013 continue;11014 if (User->getParent() == getLatch())11015 continue;11016 ReplacableUses.push_back(&U);11017 }11018 11019 // Run the updater that may introduce new uses11020 Value *NewIV = Updater(OldIV);11021 11022 // Replace the old uses with the value returned by the updater.11023 for (Use *U : ReplacableUses)11024 U->set(NewIV);11025 11026#ifndef NDEBUG11027 assertOK();11028#endif11029}11030 11031void CanonicalLoopInfo::assertOK() const {11032#ifndef NDEBUG11033 // No constraints if this object currently does not describe a loop.11034 if (!isValid())11035 return;11036 11037 BasicBlock *Preheader = getPreheader();11038 BasicBlock *Body = getBody();11039 BasicBlock *After = getAfter();11040 11041 // Verify standard control-flow we use for OpenMP loops.11042 assert(Preheader);11043 assert(isa<BranchInst>(Preheader->getTerminator()) &&11044 "Preheader must terminate with unconditional branch");11045 assert(Preheader->getSingleSuccessor() == Header &&11046 "Preheader must jump to header");11047 11048 assert(Header);11049 assert(isa<BranchInst>(Header->getTerminator()) &&11050 "Header must terminate with unconditional branch");11051 assert(Header->getSingleSuccessor() == Cond &&11052 "Header must jump to exiting block");11053 11054 assert(Cond);11055 assert(Cond->getSinglePredecessor() == Header &&11056 "Exiting block only reachable from header");11057 11058 assert(isa<BranchInst>(Cond->getTerminator()) &&11059 "Exiting block must terminate with conditional branch");11060 assert(size(successors(Cond)) == 2 &&11061 "Exiting block must have two successors");11062 assert(cast<BranchInst>(Cond->getTerminator())->getSuccessor(0) == Body &&11063 "Exiting block's first successor jump to the body");11064 assert(cast<BranchInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&11065 "Exiting block's second successor must exit the loop");11066 11067 assert(Body);11068 assert(Body->getSinglePredecessor() == Cond &&11069 "Body only reachable from exiting block");11070 assert(!isa<PHINode>(Body->front()));11071 11072 assert(Latch);11073 assert(isa<BranchInst>(Latch->getTerminator()) &&11074 "Latch must terminate with unconditional branch");11075 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");11076 // TODO: To support simple redirecting of the end of the body code that has11077 // multiple; introduce another auxiliary basic block like preheader and after.11078 assert(Latch->getSinglePredecessor() != nullptr);11079 assert(!isa<PHINode>(Latch->front()));11080 11081 assert(Exit);11082 assert(isa<BranchInst>(Exit->getTerminator()) &&11083 "Exit block must terminate with unconditional branch");11084 assert(Exit->getSingleSuccessor() == After &&11085 "Exit block must jump to after block");11086 11087 assert(After);11088 assert(After->getSinglePredecessor() == Exit &&11089 "After block only reachable from exit block");11090 assert(After->empty() || !isa<PHINode>(After->front()));11091 11092 Instruction *IndVar = getIndVar();11093 assert(IndVar && "Canonical induction variable not found?");11094 assert(isa<IntegerType>(IndVar->getType()) &&11095 "Induction variable must be an integer");11096 assert(cast<PHINode>(IndVar)->getParent() == Header &&11097 "Induction variable must be a PHI in the loop header");11098 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);11099 assert(11100 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());11101 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);11102 11103 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);11104 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);11105 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);11106 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);11107 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))11108 ->isOne());11109 11110 Value *TripCount = getTripCount();11111 assert(TripCount && "Loop trip count not found?");11112 assert(IndVar->getType() == TripCount->getType() &&11113 "Trip count and induction variable must have the same type");11114 11115 auto *CmpI = cast<CmpInst>(&Cond->front());11116 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&11117 "Exit condition must be a signed less-than comparison");11118 assert(CmpI->getOperand(0) == IndVar &&11119 "Exit condition must compare the induction variable");11120 assert(CmpI->getOperand(1) == TripCount &&11121 "Exit condition must compare with the trip count");11122#endif11123}11124 11125void CanonicalLoopInfo::invalidate() {11126 Header = nullptr;11127 Cond = nullptr;11128 Latch = nullptr;11129 Exit = nullptr;11130}11131