LLVM 24.0.0git
OMPIRBuilder.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file
9///
10/// This file implements the OpenMPIRBuilder class, which is used as a
11/// convenient way to create LLVM instructions for OpenMP directives.
12///
13//===----------------------------------------------------------------------===//
14
17#include "llvm/ADT/SmallSet.h"
20#include "llvm/ADT/StringRef.h"
31#include "llvm/IR/Attributes.h"
32#include "llvm/IR/BasicBlock.h"
33#include "llvm/IR/CFG.h"
34#include "llvm/IR/CallingConv.h"
35#include "llvm/IR/Constant.h"
36#include "llvm/IR/Constants.h"
37#include "llvm/IR/DIBuilder.h"
40#include "llvm/IR/Function.h"
42#include "llvm/IR/IRBuilder.h"
45#include "llvm/IR/LLVMContext.h"
46#include "llvm/IR/MDBuilder.h"
47#include "llvm/IR/Metadata.h"
49#include "llvm/IR/PassManager.h"
51#include "llvm/IR/Value.h"
54#include "llvm/Support/Error.h"
66
67#include <cstdint>
68#include <optional>
69
70#define DEBUG_TYPE "openmp-ir-builder"
71
72using namespace llvm;
73using namespace omp;
74
75static cl::opt<bool>
76 OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden,
77 cl::desc("Use optimistic attributes describing "
78 "'as-if' properties of runtime calls."),
79 cl::init(false));
80
82 "openmp-ir-builder-unroll-threshold-factor", cl::Hidden,
83 cl::desc("Factor for the unroll threshold to account for code "
84 "simplifications still taking place"),
85 cl::init(1.5));
86
88 "openmp-ir-builder-use-default-max-threads", cl::Hidden,
89 cl::desc("Use a default max threads if none is provided."), cl::init(true));
90
91#ifndef NDEBUG
92/// Return whether IP1 and IP2 are ambiguous, i.e. that inserting instructions
93/// at position IP1 may change the meaning of IP2 or vice-versa. This is because
94/// an InsertPoint stores the instruction before something is inserted. For
95/// instance, if both point to the same instruction, two IRBuilders alternating
96/// creating instruction will cause the instructions to be interleaved.
99 if (!IP1.isSet() || !IP2.isSet())
100 return false;
101 return IP1.getBlock() == IP2.getBlock() && IP1.getPoint() == IP2.getPoint();
102}
103
105 // Valid ordered/unordered and base algorithm combinations.
106 switch (SchedType & ~OMPScheduleType::MonotonicityMask) {
107 case OMPScheduleType::UnorderedStaticChunked:
108 case OMPScheduleType::UnorderedStatic:
109 case OMPScheduleType::UnorderedDynamicChunked:
110 case OMPScheduleType::UnorderedGuidedChunked:
111 case OMPScheduleType::UnorderedRuntime:
112 case OMPScheduleType::UnorderedAuto:
113 case OMPScheduleType::UnorderedTrapezoidal:
114 case OMPScheduleType::UnorderedGreedy:
115 case OMPScheduleType::UnorderedBalanced:
116 case OMPScheduleType::UnorderedGuidedIterativeChunked:
117 case OMPScheduleType::UnorderedGuidedAnalyticalChunked:
118 case OMPScheduleType::UnorderedSteal:
119 case OMPScheduleType::UnorderedStaticBalancedChunked:
120 case OMPScheduleType::UnorderedGuidedSimd:
121 case OMPScheduleType::UnorderedRuntimeSimd:
122 case OMPScheduleType::OrderedStaticChunked:
123 case OMPScheduleType::OrderedStatic:
124 case OMPScheduleType::OrderedDynamicChunked:
125 case OMPScheduleType::OrderedGuidedChunked:
126 case OMPScheduleType::OrderedRuntime:
127 case OMPScheduleType::OrderedAuto:
128 case OMPScheduleType::OrderdTrapezoidal:
129 case OMPScheduleType::NomergeUnorderedStaticChunked:
130 case OMPScheduleType::NomergeUnorderedStatic:
131 case OMPScheduleType::NomergeUnorderedDynamicChunked:
132 case OMPScheduleType::NomergeUnorderedGuidedChunked:
133 case OMPScheduleType::NomergeUnorderedRuntime:
134 case OMPScheduleType::NomergeUnorderedAuto:
135 case OMPScheduleType::NomergeUnorderedTrapezoidal:
136 case OMPScheduleType::NomergeUnorderedGreedy:
137 case OMPScheduleType::NomergeUnorderedBalanced:
138 case OMPScheduleType::NomergeUnorderedGuidedIterativeChunked:
139 case OMPScheduleType::NomergeUnorderedGuidedAnalyticalChunked:
140 case OMPScheduleType::NomergeUnorderedSteal:
141 case OMPScheduleType::NomergeOrderedStaticChunked:
142 case OMPScheduleType::NomergeOrderedStatic:
143 case OMPScheduleType::NomergeOrderedDynamicChunked:
144 case OMPScheduleType::NomergeOrderedGuidedChunked:
145 case OMPScheduleType::NomergeOrderedRuntime:
146 case OMPScheduleType::NomergeOrderedAuto:
147 case OMPScheduleType::NomergeOrderedTrapezoidal:
148 case OMPScheduleType::OrderedDistributeChunked:
149 case OMPScheduleType::OrderedDistribute:
150 break;
151 default:
152 return false;
153 }
154
155 // Must not set both monotonicity modifiers at the same time.
156 OMPScheduleType MonotonicityFlags =
157 SchedType & OMPScheduleType::MonotonicityMask;
158 if (MonotonicityFlags == OMPScheduleType::MonotonicityMask)
159 return false;
160
161 return true;
162}
163#endif
164
165/// This is a wrapper over IRBuilderBase::restoreIP that also restores a current
166/// debug location when the insert point is at the end of a block. It picks a
167/// location scoped to the current function: the block's last instruction
168/// location if the block is non-empty, otherwise a location synthesized from
169/// the function's subprogram (when the function has debug info).
172 Builder.restoreIP(IP);
173 // When IP points at a real instruction, restoreIP (SetInsertPoint) already
174 // set the debug location from that instruction, so leave it alone.
175 llvm::BasicBlock *BB = Builder.GetInsertBlock();
176 if (Builder.GetInsertPoint() != BB->end())
177 return;
178
179 // At the end of a block, pick a location guaranteed to belong to the current
180 // insertion function's subprogram. Prefer the block's own last instruction;
181 // otherwise synthesize a location from the function's subprogram.
182 if (!BB->empty())
183 Builder.SetCurrentDebugLocation(BB->back().getStableDebugLoc());
184 else if (llvm::DISubprogram *FSP =
185 BB->getParent() ? BB->getParent()->getSubprogram() : nullptr) {
186 unsigned Line = FSP->getScopeLine() ? FSP->getScopeLine() : FSP->getLine();
187 Builder.SetCurrentDebugLocation(
188 llvm::DILocation::get(FSP->getContext(), Line, /*Column=*/0, FSP));
189 }
190}
191
192static bool hasGridValue(const Triple &T) {
193 return T.isAMDGPU() || T.isNVPTX() || T.isSPIRV();
194}
195
196static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {
197 if (T.isAMDGPU()) {
198 StringRef Features =
199 Kernel->getFnAttribute("target-features").getValueAsString();
200 if (Features.count("+wavefrontsize64"))
203 }
204 if (T.isNVPTX())
206 if (T.isSPIRV())
208 llvm_unreachable("No grid value available for this architecture!");
209}
210
211/// Determine which scheduling algorithm to use, determined from schedule clause
212/// arguments.
213static OMPScheduleType
214getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,
215 bool HasSimdModifier, bool HasDistScheduleChunks) {
216 // Currently, the default schedule it static.
217 switch (ClauseKind) {
218 case OMP_SCHEDULE_Default:
219 case OMP_SCHEDULE_Static:
220 return HasChunks ? OMPScheduleType::BaseStaticChunked
221 : OMPScheduleType::BaseStatic;
222 case OMP_SCHEDULE_Dynamic:
223 return OMPScheduleType::BaseDynamicChunked;
224 case OMP_SCHEDULE_Guided:
225 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd
226 : OMPScheduleType::BaseGuidedChunked;
227 case OMP_SCHEDULE_Auto:
229 case OMP_SCHEDULE_Runtime:
230 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd
231 : OMPScheduleType::BaseRuntime;
232 case OMP_SCHEDULE_Distribute:
233 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked
234 : OMPScheduleType::BaseDistribute;
235 }
236 llvm_unreachable("unhandled schedule clause argument");
237}
238
239/// Adds ordering modifier flags to schedule type.
240static OMPScheduleType
242 bool HasOrderedClause) {
243 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==
244 OMPScheduleType::None &&
245 "Must not have ordering nor monotonicity flags already set");
246
247 OMPScheduleType OrderingModifier = HasOrderedClause
248 ? OMPScheduleType::ModifierOrdered
249 : OMPScheduleType::ModifierUnordered;
250 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;
251
252 // Unsupported combinations
253 if (OrderingScheduleType ==
254 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))
255 return OMPScheduleType::OrderedGuidedChunked;
256 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |
257 OMPScheduleType::ModifierOrdered))
258 return OMPScheduleType::OrderedRuntime;
259
260 return OrderingScheduleType;
261}
262
263/// Adds monotonicity modifier flags to schedule type.
264static OMPScheduleType
266 bool HasSimdModifier, bool HasMonotonic,
267 bool HasNonmonotonic, bool HasOrderedClause) {
268 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==
269 OMPScheduleType::None &&
270 "Must not have monotonicity flags already set");
271 assert((!HasMonotonic || !HasNonmonotonic) &&
272 "Monotonic and Nonmonotonic are contradicting each other");
273
274 if (HasMonotonic) {
275 return ScheduleType | OMPScheduleType::ModifierMonotonic;
276 } else if (HasNonmonotonic) {
277 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
278 } else {
279 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.
280 // If the static schedule kind is specified or if the ordered clause is
281 // specified, and if the nonmonotonic modifier is not specified, the
282 // effect is as if the monotonic modifier is specified. Otherwise, unless
283 // the monotonic modifier is specified, the effect is as if the
284 // nonmonotonic modifier is specified.
285 OMPScheduleType BaseScheduleType =
286 ScheduleType & ~OMPScheduleType::ModifierMask;
287 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||
288 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||
289 HasOrderedClause) {
290 // The monotonic is used by default in openmp runtime library, so no need
291 // to set it.
292 return ScheduleType;
293 } else {
294 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
295 }
296 }
297}
298
299/// Determine the schedule type using schedule and ordering clause arguments.
300static OMPScheduleType
301computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,
302 bool HasSimdModifier, bool HasMonotonicModifier,
303 bool HasNonmonotonicModifier, bool HasOrderedClause,
304 bool HasDistScheduleChunks) {
306 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);
307 OMPScheduleType OrderedSchedule =
308 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);
310 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,
311 HasNonmonotonicModifier, HasOrderedClause);
312
314 return Result;
315}
316
317/// Given a function, if it represents the entry point of a target kernel, this
318/// returns the execution mode flags associated with that kernel.
319static std::optional<omp::OMPTgtExecModeFlags>
321 CallInst *TargetInitCall = nullptr;
322 for (Instruction &Inst : Kernel.getEntryBlock()) {
323 if (auto *Call = dyn_cast<CallInst>(&Inst)) {
324 if (Call->getCalledFunction()->getName() == "__kmpc_target_init") {
325 TargetInitCall = Call;
326 break;
327 }
328 }
329 }
330
331 if (!TargetInitCall)
332 return std::nullopt;
333
334 // Get the kernel mode information from the global variable associated to the
335 // first argument to the call to __kmpc_target_init. Refer to
336 // createTargetInit() to see how this is initialized.
337 Value *InitOperand = TargetInitCall->getArgOperand(0);
338 GlobalVariable *KernelEnv = nullptr;
339 if (auto *Cast = dyn_cast<ConstantExpr>(InitOperand))
340 KernelEnv = cast<GlobalVariable>(Cast->getOperand(0));
341 else
342 KernelEnv = cast<GlobalVariable>(InitOperand);
343 auto *KernelEnvInit = cast<ConstantStruct>(KernelEnv->getInitializer());
344 auto *ConfigEnv = cast<ConstantStruct>(KernelEnvInit->getOperand(0));
345 auto *KernelMode = cast<ConstantInt>(ConfigEnv->getOperand(2));
346 return static_cast<OMPTgtExecModeFlags>(KernelMode->getZExtValue());
347}
348
349static bool isGenericKernel(Function &Fn) {
350 std::optional<omp::OMPTgtExecModeFlags> ExecMode =
352 return !ExecMode || (*ExecMode & OMP_TGT_EXEC_MODE_GENERIC);
353}
354
355/// Make \p Source branch to \p Target.
356///
357/// Handles two situations:
358/// * \p Source already has an unconditional branch.
359/// * \p Source is a degenerate block (no terminator because the BB is
360/// the current head of the IR construction).
362 if (Instruction *Term = Source->getTerminatorOrNull()) {
363 auto *Br = cast<UncondBrInst>(Term);
364 BasicBlock *Succ = Br->getSuccessor();
365 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);
366 Br->setSuccessor(Target);
367 return;
368 }
369
370 auto *NewBr = UncondBrInst::Create(Target, Source);
371 NewBr->setDebugLoc(DL);
372}
373
375 bool CreateBranch, DebugLoc DL) {
376 assert(New->getFirstInsertionPt() == New->begin() &&
377 "Target BB must not have PHI nodes");
378
379 // Move instructions to new block.
380 BasicBlock *Old = IP.getBlock();
381 // If the `Old` block is empty then there are no instructions to move. But in
382 // the new debug scheme, it could have trailing debug records which will be
383 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2
384 // reasons:
385 // 1. If `New` is also empty, `BasicBlock::splice` crashes.
386 // 2. Even if `New` is not empty, the rationale to move those records to `New`
387 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function
388 // assumes that `Old` is optimized out and is going away. This is not the case
389 // here. The `Old` block is still being used e.g. a branch instruction is
390 // added to it later in this function.
391 // So we call `BasicBlock::splice` only when `Old` is not empty.
392 if (!Old->empty())
393 New->splice(New->begin(), Old, IP.getPoint(), Old->end());
394
395 if (CreateBranch) {
396 auto *NewBr = UncondBrInst::Create(New, Old);
397 NewBr->setDebugLoc(DL);
398 }
399}
400
401void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {
402 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
403 BasicBlock *Old = Builder.GetInsertBlock();
404
405 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);
406 if (CreateBranch)
407 Builder.SetInsertPoint(Old->getTerminator());
408 else
409 Builder.SetInsertPoint(Old);
410
411 // SetInsertPoint also updates the Builder's debug location, but we want to
412 // keep the one the Builder was configured to use.
413 Builder.SetCurrentDebugLocation(DebugLoc);
414}
415
417 DebugLoc DL, llvm::Twine Name) {
418 BasicBlock *Old = IP.getBlock();
420 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,
421 Old->getParent(), Old->getNextNode());
422 spliceBB(IP, New, CreateBranch, DL);
423 New->replaceSuccessorsPhiUsesWith(Old, New);
424 return New;
425}
426
427BasicBlock *llvm::splitBB(IRBuilderBase &Builder, bool CreateBranch,
428 llvm::Twine Name) {
429 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
430 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
431 if (CreateBranch)
432 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
433 else
434 Builder.SetInsertPoint(Builder.GetInsertBlock());
435 // SetInsertPoint also updates the Builder's debug location, but we want to
436 // keep the one the Builder was configured to use.
437 Builder.SetCurrentDebugLocation(DebugLoc);
438 return New;
439}
440
441BasicBlock *llvm::splitBB(IRBuilder<> &Builder, bool CreateBranch,
442 llvm::Twine Name) {
443 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
444 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
445 if (CreateBranch)
446 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
447 else
448 Builder.SetInsertPoint(Builder.GetInsertBlock());
449 // SetInsertPoint also updates the Builder's debug location, but we want to
450 // keep the one the Builder was configured to use.
451 Builder.SetCurrentDebugLocation(DebugLoc);
452 return New;
453}
454
456 llvm::Twine Suffix) {
457 BasicBlock *Old = Builder.GetInsertBlock();
458 return splitBB(Builder, CreateBranch, Old->getName() + Suffix);
459}
460
461// This function creates a fake integer value and a fake use for the integer
462// value. It returns the fake value created. This is useful in modeling the
463// extra arguments to the outlined functions.
465 OpenMPIRBuilder::InsertPointTy OuterAllocaIP,
467 OpenMPIRBuilder::InsertPointTy InnerAllocaIP,
468 const Twine &Name = "", bool AsPtr = true,
469 bool Is64Bit = false) {
470 Builder.restoreIP(OuterAllocaIP);
471 IntegerType *IntTy = Is64Bit ? Builder.getInt64Ty() : Builder.getInt32Ty();
472 Instruction *FakeVal;
473 AllocaInst *FakeValAddr =
474 Builder.CreateAlloca(IntTy, nullptr, Name + ".addr");
475 ToBeDeleted.push_back(FakeValAddr);
476
477 if (AsPtr) {
478 FakeVal = FakeValAddr;
479 } else {
480 FakeVal = Builder.CreateLoad(IntTy, FakeValAddr, Name + ".val");
481 ToBeDeleted.push_back(FakeVal);
482 }
483
484 // Generate a fake use of this value
485 Builder.restoreIP(InnerAllocaIP);
486 Instruction *UseFakeVal;
487 if (AsPtr) {
488 UseFakeVal = Builder.CreateLoad(IntTy, FakeVal, Name + ".use");
489 } else {
490 UseFakeVal = cast<BinaryOperator>(Builder.CreateAdd(
491 FakeVal, Is64Bit ? Builder.getInt64(10) : Builder.getInt32(10)));
492 }
493 ToBeDeleted.push_back(UseFakeVal);
494 return FakeVal;
495}
496
497//===----------------------------------------------------------------------===//
498// OpenMPIRBuilderConfig
499//===----------------------------------------------------------------------===//
500
501namespace {
503/// Values for bit flags for marking which requires clauses have been used.
504enum OpenMPOffloadingRequiresDirFlags {
505 /// flag undefined.
506 OMP_REQ_UNDEFINED = 0x000,
507 /// no requires directive present.
508 OMP_REQ_NONE = 0x001,
509 /// reverse_offload clause.
510 OMP_REQ_REVERSE_OFFLOAD = 0x002,
511 /// unified_address clause.
512 OMP_REQ_UNIFIED_ADDRESS = 0x004,
513 /// unified_shared_memory clause.
514 OMP_REQ_UNIFIED_SHARED_MEMORY = 0x008,
515 /// dynamic_allocators clause.
516 OMP_REQ_DYNAMIC_ALLOCATORS = 0x010,
517 LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)
518};
519
520class OMPCodeExtractor : public CodeExtractor {
521public:
522 OMPCodeExtractor(OpenMPIRBuilder &OMPBuilder, ArrayRef<BasicBlock *> BBs,
523 DominatorTree *DT = nullptr, bool AggregateArgs = false,
524 BlockFrequencyInfo *BFI = nullptr,
525 BranchProbabilityInfo *BPI = nullptr,
526 AssumptionCache *AC = nullptr, bool AllowVarArgs = false,
527 bool AllowAlloca = false,
528 BasicBlock *AllocationBlock = nullptr,
529 ArrayRef<BasicBlock *> DeallocationBlocks = {},
530 std::string Suffix = "", bool ArgsInZeroAddressSpace = false)
531 : CodeExtractor(BBs, DT, AggregateArgs, BFI, BPI, AC, AllowVarArgs,
532 AllowAlloca, AllocationBlock, DeallocationBlocks, Suffix,
533 ArgsInZeroAddressSpace),
534 OMPBuilder(OMPBuilder) {}
535
536 virtual ~OMPCodeExtractor() = default;
537
538protected:
539 OpenMPIRBuilder &OMPBuilder;
540};
541
542class DeviceSharedMemCodeExtractor : public OMPCodeExtractor {
543public:
544 using OMPCodeExtractor::OMPCodeExtractor;
545 virtual ~DeviceSharedMemCodeExtractor() = default;
546
547protected:
548 virtual Instruction *
549 allocateVar(IRBuilder<>::InsertPoint AllocaIP, Type *VarType,
550 const Twine &Name = Twine(""),
551 AddrSpaceCastInst **CastedAlloc = nullptr) override {
552 return OMPBuilder.createOMPAllocShared(AllocaIP, VarType, Name);
553 }
554
555 virtual Instruction *deallocateVar(IRBuilder<>::InsertPoint DeallocIP,
556 Value *Var, Type *VarType) override {
557 return OMPBuilder.createOMPFreeShared(DeallocIP, Var, VarType);
558 }
559};
560
561/// Helper storing information about regions to outline using device shared
562/// memory for intermediate allocations.
563struct DeviceSharedMemOutlineInfo : public OpenMPIRBuilder::OutlineInfo {
564 OpenMPIRBuilder &OMPBuilder;
565
566 DeviceSharedMemOutlineInfo(OpenMPIRBuilder &OMPBuilder)
567 : OMPBuilder(OMPBuilder) {}
568 virtual ~DeviceSharedMemOutlineInfo() = default;
569
570 virtual std::unique_ptr<CodeExtractor>
571 createCodeExtractor(ArrayRef<BasicBlock *> Blocks,
572 bool ArgsInZeroAddressSpace,
573 Twine Suffix = Twine("")) override;
574};
575
576} // anonymous namespace
577
579 : RequiresFlags(OMP_REQ_UNDEFINED) {}
580
583 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,
584 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)
587 RequiresFlags(OMP_REQ_UNDEFINED) {
588 if (HasRequiresReverseOffload)
589 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
590 if (HasRequiresUnifiedAddress)
591 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
592 if (HasRequiresUnifiedSharedMemory)
593 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
594 if (HasRequiresDynamicAllocators)
595 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
596}
597
599 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;
600}
601
603 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;
604}
605
607 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;
608}
609
611 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;
612}
613
615 return hasRequiresFlags() ? RequiresFlags
616 : static_cast<int64_t>(OMP_REQ_NONE);
617}
618
620 if (Value)
621 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
622 else
623 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;
624}
625
627 if (Value)
628 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
629 else
630 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;
631}
632
634 if (Value)
635 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
636 else
637 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;
638}
639
641 if (Value)
642 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
643 else
644 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;
645}
646
647//===----------------------------------------------------------------------===//
648// OpenMPIRBuilder
649//===----------------------------------------------------------------------===//
650
653 SmallVector<Value *> &ArgsVector) {
655 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);
656 auto Int32Ty = Type::getInt32Ty(Builder.getContext());
657 constexpr size_t MaxDim = 3;
658 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));
659
660 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);
661
662 Value *DynCGroupMemFallbackFlag =
663 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));
664 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);
665
666 Value *StrictBlocksFlag = Builder.getInt64(KernelArgs.StrictBlocks);
667 Value *StrictThreadsFlag = Builder.getInt64(KernelArgs.StrictThreads);
668
669 StrictBlocksFlag = Builder.CreateShl(StrictBlocksFlag, 6);
670 StrictThreadsFlag = Builder.CreateShl(StrictThreadsFlag, 7);
671
672 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
673 Flags = Builder.CreateOr(Flags, StrictBlocksFlag);
674 Flags = Builder.CreateOr(Flags, StrictThreadsFlag);
675
676 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
677
678 Value *NumTeams3D =
679 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
680 Value *NumThreads3D =
681 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
682 for (unsigned I :
683 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
684 NumTeams3D =
685 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
686 for (unsigned I :
687 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
688 NumThreads3D =
689 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
690
691 ArgsVector = {Version,
692 PointerNum,
693 KernelArgs.RTArgs.BasePointersArray,
694 KernelArgs.RTArgs.PointersArray,
695 KernelArgs.RTArgs.SizesArray,
696 KernelArgs.RTArgs.MapTypesArray,
697 KernelArgs.RTArgs.MapNamesArray,
698 KernelArgs.RTArgs.MappersArray,
699 KernelArgs.NumIterations,
700 Flags,
701 NumTeams3D,
702 NumThreads3D,
703 KernelArgs.DynCGroupMem};
704}
705
707 LLVMContext &Ctx = Fn.getContext();
708
709 // Get the function's current attributes.
710 auto Attrs = Fn.getAttributes();
711 auto FnAttrs = Attrs.getFnAttrs();
712 auto RetAttrs = Attrs.getRetAttrs();
714 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
715 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
716
717 // Add AS to FnAS while taking special care with integer extensions.
718 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
719 bool Param = true) -> void {
720 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
721 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
722 if (HasSignExt || HasZeroExt) {
723 assert(AS.getNumAttributes() == 1 &&
724 "Currently not handling extension attr combined with others.");
725 if (Param) {
726 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
727 FnAS = FnAS.addAttribute(Ctx, AK);
728 } else if (auto AK =
729 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
730 FnAS = FnAS.addAttribute(Ctx, AK);
731 } else {
732 FnAS = FnAS.addAttributes(Ctx, AS);
733 }
734 };
735
736#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
737#include "llvm/Frontend/OpenMP/OMPKinds.def"
738
739 // Add attributes to the function declaration.
740 switch (FnID) {
741#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
742 case Enum: \
743 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
744 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
745 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
746 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
747 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
748 break;
749#include "llvm/Frontend/OpenMP/OMPKinds.def"
750 default:
751 // Attributes are optional.
752 break;
753 }
754}
755
758 FunctionType *FnTy = nullptr;
759 Function *Fn = nullptr;
760
761 // Try to find the declation in the module first.
762 switch (FnID) {
763#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
764 case Enum: \
765 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
766 IsVarArg); \
767 Fn = M.getFunction(Str); \
768 break;
769#include "llvm/Frontend/OpenMP/OMPKinds.def"
770 }
771
772 if (!Fn) {
773 // Create a new declaration if we need one.
774 switch (FnID) {
775#define OMP_RTL(Enum, Str, ...) \
776 case Enum: \
777 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
778 break;
779#include "llvm/Frontend/OpenMP/OMPKinds.def"
780 }
781 Fn->setCallingConv(Config.getRuntimeCC());
782 // Add information if the runtime function takes a callback function
783 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
784 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
785 LLVMContext &Ctx = Fn->getContext();
786 MDBuilder MDB(Ctx);
787 // Annotate the callback behavior of the runtime function:
788 // - The callback callee is argument number 2 (microtask).
789 // - The first two arguments of the callback callee are unknown (-1).
790 // - All variadic arguments to the runtime function are passed to the
791 // callback callee.
792 Fn->addMetadata(
793 LLVMContext::MD_callback,
795 2, {-1, -1}, /* VarArgsArePassed */ true)}));
796 }
797 }
798
799 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
800 << " with type " << *Fn->getFunctionType() << "\n");
801 addAttributes(FnID, *Fn);
802
803 } else {
804 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
805 << " with type " << *Fn->getFunctionType() << "\n");
806 }
807
808 assert(Fn && "Failed to create OpenMP runtime function");
809
810 return {FnTy, Fn};
811}
812
815 if (!FiniBB) {
816 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
818 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
819 Builder.SetInsertPoint(FiniBB);
820 // FiniCB adds the branch to the exit stub.
821 if (Error Err = FiniCB(Builder.saveIP()))
822 return Err;
823 }
824 return FiniBB;
825}
826
828 BasicBlock *OtherFiniBB) {
829 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
830 if (!FiniBB) {
831 FiniBB = OtherFiniBB;
832
833 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
834 if (Error Err = FiniCB(Builder.saveIP()))
835 return Err;
836
837 return Error::success();
838 }
839
840 // Move instructions from FiniBB to the start of OtherFiniBB.
841 auto EndIt = FiniBB->end();
842 if (FiniBB->size() >= 1)
843 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
844 EndIt = Prev;
845 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
846 EndIt);
847
848 FiniBB->replaceAllUsesWith(OtherFiniBB);
849 FiniBB->eraseFromParent();
850 FiniBB = OtherFiniBB;
851 return Error::success();
852}
853
856 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
857 assert(Fn && "Failed to create OpenMP runtime function pointer");
858 return Fn;
859}
860
863 StringRef Name) {
864 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
865 Call->setCallingConv(Config.getRuntimeCC());
866 return Call;
867}
868
869void OpenMPIRBuilder::initialize() { initializeTypes(M); }
870
873 BasicBlock &EntryBlock = Function->getEntryBlock();
874 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
875
876 // Loop over blocks looking for constant allocas, skipping the entry block
877 // as any allocas there are already in the desired location.
878 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
879 Block++) {
880 for (auto Inst = Block->getReverseIterator()->begin();
881 Inst != Block->getReverseIterator()->end();) {
883 Inst++;
885 continue;
886 AllocaInst->moveBeforePreserving(MoveLocInst);
887 } else {
888 Inst++;
889 }
890 }
891 }
892}
893
896
897 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
898 // TODO: For now, we support simple static allocations, we might need to
899 // move non-static ones as well. However, this will need further analysis to
900 // move the lenght arguments as well.
902 };
903
904 for (llvm::Instruction &Inst : Block)
906 if (ShouldHoistAlloca(*AllocaInst))
907 AllocasToMove.push_back(AllocaInst);
908
909 auto InsertPoint =
910 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
911
912 for (llvm::Instruction *AllocaInst : AllocasToMove)
914}
915
917 PostDominatorTree PostDomTree(*Func);
918 for (llvm::BasicBlock &BB : *Func)
919 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
921}
922
924 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
926 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
927 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
928 // Skip functions that have not finalized yet; may happen with nested
929 // function generation.
930 if (Fn && OI->getFunction() != Fn) {
931 DeferredOutlines.push_back(std::move(OI));
932 continue;
933 }
934
935 ParallelRegionBlockSet.clear();
936 Blocks.clear();
937 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
938
939 Function *OuterFn = OI->getFunction();
940 CodeExtractorAnalysisCache CEAC(*OuterFn);
941 // If we generate code for the target device, we need to allocate
942 // struct for aggregate params in the device default alloca address space.
943 // OpenMP runtime requires that the params of the extracted functions are
944 // passed as zero address space pointers. This flag ensures that
945 // CodeExtractor generates correct code for extracted functions
946 // which are used by OpenMP runtime.
947 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
948 std::unique_ptr<CodeExtractor> Extractor =
949 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
950
951 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
952 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
953 << " Exit: " << OI->ExitBB->getName() << "\n");
954 assert(Extractor->isEligible() &&
955 "Expected OpenMP outlining to be possible!");
956
957 for (auto *V : OI->ExcludeArgsFromAggregate)
958 Extractor->excludeArgFromAggregate(V);
959
960 Function *OutlinedFn =
961 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
962
963 // Forward target-cpu, target-features attributes to the outlined function.
964 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
965 if (TargetCpuAttr.isStringAttribute())
966 OutlinedFn->addFnAttr(TargetCpuAttr);
967
968 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
969 if (TargetFeaturesAttr.isStringAttribute())
970 OutlinedFn->addFnAttr(TargetFeaturesAttr);
971
972 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
973 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
974 assert(OutlinedFn->getReturnType()->isVoidTy() &&
975 "OpenMP outlined functions should not return a value!");
976
977 // For compability with the clang CG we move the outlined function after the
978 // one with the parallel region.
979 OutlinedFn->removeFromParent();
980 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
981
982 // Remove the artificial entry introduced by the extractor right away, we
983 // made our own entry block after all.
984 {
985 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
986 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
987 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
988 // Move instructions from the to-be-deleted ArtificialEntry to the entry
989 // basic block of the parallel region. CodeExtractor generates
990 // instructions to unwrap the aggregate argument and may sink
991 // allocas/bitcasts for values that are solely used in the outlined region
992 // and do not escape.
993 assert(!ArtificialEntry.empty() &&
994 "Expected instructions to add in the outlined region entry");
995 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
996 End = ArtificialEntry.rend();
997 It != End;) {
998 Instruction &I = *It;
999 It++;
1000
1001 if (I.isTerminator()) {
1002 // Absorb any debug value that terminator may have
1003 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
1004 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
1005 continue;
1006 }
1007
1008 I.moveBeforePreserving(*OI->EntryBB,
1009 OI->EntryBB->getFirstInsertionPt());
1010 }
1011
1012 OI->EntryBB->moveBefore(&ArtificialEntry);
1013 ArtificialEntry.eraseFromParent();
1014 }
1015 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
1016 assert(OutlinedFn && OutlinedFn->hasNUses(1));
1017
1018 // Run a user callback, e.g. to add attributes.
1019 if (OI->PostOutlineCB)
1020 OI->PostOutlineCB(*OutlinedFn);
1021
1022 if (OI->FixUpNonEntryAllocas)
1024 }
1025
1026 // Remove work items that have been completed.
1027 OutlineInfos = std::move(DeferredOutlines);
1028
1029 // The createTarget functions embeds user written code into
1030 // the target region which may inject allocas which need to
1031 // be moved to the entry block of our target or risk malformed
1032 // optimisations by later passes, this is only relevant for
1033 // the device pass which appears to be a little more delicate
1034 // when it comes to optimisations (however, we do not block on
1035 // that here, it's up to the inserter to the list to do so).
1036 // This notbaly has to occur after the OutlinedInfo candidates
1037 // have been extracted so we have an end product that will not
1038 // be implicitly adversely affected by any raises unless
1039 // intentionally appended to the list.
1040 // NOTE: This only does so for ConstantData, it could be extended
1041 // to ConstantExpr's with further effort, however, they should
1042 // largely be folded when they get here. Extending it to runtime
1043 // defined/read+writeable allocation sizes would be non-trivial
1044 // (need to factor in movement of any stores to variables the
1045 // allocation size depends on, as well as the usual loads,
1046 // otherwise it'll yield the wrong result after movement) and
1047 // likely be more suitable as an LLVM optimisation pass.
1050
1051 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1052 [](EmitMetadataErrorKind Kind,
1053 const TargetRegionEntryInfo &EntryInfo) -> void {
1054 errs() << "Error of kind: " << Kind
1055 << " when emitting offload entries and metadata during "
1056 "OMPIRBuilder finalization \n";
1057 };
1058
1059 if (!OffloadInfoManager.empty())
1061
1062 // Rewrite uses of globals to their replacement declare target globals if
1063 // we are processing a device module.
1064 if (Config.isTargetDevice())
1065 applyDeclareTargetGlobalReplacements();
1066
1067 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1068 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1069 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1070 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1071 }
1072
1073 IsFinalized = true;
1074}
1075
1076bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1077
1079 GlobalValue *Original, GlobalValue *Replacement) {
1080 assert(Original && Replacement &&
1081 "Null values provided to registerDeclareTargetGlobalReplacement");
1082 DeclareTargetGlobalReplacements.push_back({Original, Replacement});
1083}
1084
1085void OpenMPIRBuilder::applyDeclareTargetGlobalReplacements() {
1086 for (DeclareTargetGlobalReplacement &R : DeclareTargetGlobalReplacements) {
1087 GlobalValue *OldGV = R.Original;
1088 GlobalValue *NewGV = R.Replacement;
1089
1090 assert(OldGV && NewGV &&
1091 "A null value was inserted into DeclareTargetGlobalReplacements");
1092
1093 // The assert above should catch this case, but this is kept to attempt
1094 // to proceed without issue when asserts are off.
1095 if (!OldGV || !NewGV)
1096 continue;
1097
1098 // The replacement global is a reference pointer that holds the
1099 // address of the device-resident storage. Every use must load the
1100 // reference pointer first and use the loaded address.
1101 //
1102 // Constant expression users (e.g. a constant GEP embedded in another
1103 // global's initializer or in an instruction) cannot have a load inserted
1104 // in place, so first expand any constant-expression users that live inside
1105 // functions into instructions. Any remaining constant users are handled
1106 // via a direct constant rewrite below as we cannot materialize a load
1107 // there.
1108 //
1109 // NOTE: We extend the constant rewrite to module scope, as we replace all
1110 // usages.
1111 if (auto *OldConst = dyn_cast<Constant>(OldGV))
1113 /*RestrictToFunc=*/nullptr,
1114 /*RemoveDeadConstants=*/false);
1115
1116 IRBuilderBase::InsertPointGuard Guard(Builder);
1118 for (User *U : Users) {
1119 auto *Insn = dyn_cast<Instruction>(U);
1120 if (!Insn)
1121 continue;
1122
1123 // A PHI node cannot have a load inserted immediately before it, as PHIs
1124 // must remain grouped at the top of their basic block. So we need to
1125 // make sure any loads we emit are generated in the preceding edge, a
1126 // PHI may reference the global on more than one edge, so every matching
1127 // slot must be handled.
1128 if (auto *PHI = dyn_cast<PHINode>(Insn)) {
1129 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I < E; ++I) {
1130 if (PHI->getIncomingValue(I) != OldGV)
1131 continue;
1132
1133 BasicBlock *IncomingBB = PHI->getIncomingBlock(I);
1134 Builder.SetInsertPoint(IncomingBB->getTerminator());
1135 Builder.SetCurrentDebugLocation(PHI->getDebugLoc());
1136 LoadInst *EdgeLoad = Builder.CreateLoad(NewGV->getType(), NewGV);
1137 PHI->setIncomingValue(I, EdgeLoad);
1138 }
1139 continue;
1140 }
1141
1142 Builder.SetInsertPoint(Insn);
1143 Builder.SetCurrentDebugLocation(Insn->getDebugLoc());
1144 LoadInst *Load = Builder.CreateLoad(NewGV->getType(), NewGV);
1145
1146 // The replacement declare target global lives in the default address
1147 // space, whereas the original global may reside in a non-default
1148 // address space. In that case the initial lowering may have
1149 // emitted an addrspacecast that is no longer valid. Replace the
1150 // whole addrspacecast with the load and erase it rather than
1151 // feeding the load back into the (now pointless) cast.
1152 // NOTE: If we end up with replacement declare target globals in
1153 // non-zero AS's the below will need some minor extensions to have the
1154 // option to alter the address space cast to the new address space where
1155 // required rather than just replacing it.
1156 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Insn)) {
1157 unsigned NewGVAS = NewGV->getType()->getPointerAddressSpace();
1158 assert(NewGVAS == 0 &&
1159 "Non-default address space declare target global");
1160 unsigned OldGVAS = OldGV->getType()->getPointerAddressSpace();
1161 unsigned DestAS = ASC->getType()->getPointerAddressSpace();
1162 if (DestAS == 0 && NewGVAS != OldGVAS) {
1163 ASC->replaceAllUsesWith(Load);
1164 ASC->eraseFromParent();
1165 continue;
1166 }
1167 }
1168
1169 Insn->replaceUsesOfWith(OldGV, Load);
1170 }
1171 }
1172
1174}
1175
1177 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1178}
1179
1181 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1182 auto *GV =
1183 new GlobalVariable(M, I32Ty,
1184 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1185 ConstantInt::get(I32Ty, Value), Name);
1186 GV->setVisibility(GlobalValue::HiddenVisibility);
1187
1188 return GV;
1189}
1190
1192 if (List.empty())
1193 return;
1194
1195 // Convert List to what ConstantArray needs.
1197 UsedArray.resize(List.size());
1198 for (unsigned I = 0, E = List.size(); I != E; ++I)
1200 cast<Constant>(&*List[I]), Builder.getPtrTy());
1201
1202 if (UsedArray.empty())
1203 return;
1204 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1205
1206 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1207 ConstantArray::get(ATy, UsedArray), Name);
1208
1209 GV->setSection("llvm.metadata");
1210}
1211
1214 OMPTgtExecModeFlags Mode) {
1215 auto *Int8Ty = Builder.getInt8Ty();
1216 auto *GVMode = new GlobalVariable(
1217 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1218 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1219 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1220 return GVMode;
1221}
1222
1224 uint32_t SrcLocStrSize,
1225 IdentFlag LocFlags,
1226 unsigned Reserve2Flags) {
1227 // Enable "C-mode".
1228 LocFlags |= OMP_IDENT_FLAG_KMPC;
1229
1230 Constant *&Ident =
1231 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1232 if (!Ident) {
1233 Constant *I32Null = ConstantInt::getNullValue(Int32);
1234 Constant *IdentData[] = {I32Null,
1235 ConstantInt::get(Int32, uint32_t(LocFlags)),
1236 ConstantInt::get(Int32, Reserve2Flags),
1237 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1238
1239 size_t SrcLocStrArgIdx = 4;
1240 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1242 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1243 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1244 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1245 Constant *Initializer =
1246 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1247
1248 // Look for existing encoding of the location + flags, not needed but
1249 // minimizes the difference to the existing solution while we transition.
1250 for (GlobalVariable &GV : M.globals())
1251 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1252 if (GV.getInitializer() == Initializer)
1253 Ident = &GV;
1254
1255 if (!Ident) {
1256 auto *GV = new GlobalVariable(
1257 M, OpenMPIRBuilder::Ident,
1258 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1260 M.getDataLayout().getDefaultGlobalsAddressSpace());
1261 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1262 GV->setAlignment(Align(8));
1263 Ident = GV;
1264 }
1265 }
1266
1267 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1268}
1269
1271 uint32_t &SrcLocStrSize) {
1272 SrcLocStrSize = LocStr.size();
1273 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1274 if (!SrcLocStr) {
1275 Constant *Initializer =
1276 ConstantDataArray::getString(M.getContext(), LocStr);
1277
1278 // Look for existing encoding of the location, not needed but minimizes the
1279 // difference to the existing solution while we transition.
1280 for (GlobalVariable &GV : M.globals())
1281 if (GV.isConstant() && GV.hasInitializer() &&
1282 GV.getInitializer() == Initializer)
1283 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1284
1285 SrcLocStr = Builder.CreateGlobalString(
1286 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1287 &M);
1288 }
1289 return SrcLocStr;
1290}
1291
1293 StringRef FileName,
1294 unsigned Line, unsigned Column,
1295 uint32_t &SrcLocStrSize) {
1296 SmallString<128> Buffer;
1297 Buffer.push_back(';');
1298 Buffer.append(FileName);
1299 Buffer.push_back(';');
1300 Buffer.append(FunctionName);
1301 Buffer.push_back(';');
1302 Buffer.append(std::to_string(Line));
1303 Buffer.push_back(';');
1304 Buffer.append(std::to_string(Column));
1305 Buffer.push_back(';');
1306 Buffer.push_back(';');
1307 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1308}
1309
1310Constant *
1312 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1313 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1314}
1315
1317 uint32_t &SrcLocStrSize,
1318 Function *F) {
1319 DILocation *DIL = DL.get();
1320 if (!DIL)
1321 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1322 StringRef FileName =
1323 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1324 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1325 if (Function.empty() && F)
1326 Function = F->getName();
1327 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1328 DIL->getColumn(), SrcLocStrSize);
1329}
1330
1332 uint32_t &SrcLocStrSize) {
1333 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1334 Loc.IP.getBlock()->getParent());
1335}
1336
1339 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1340 "omp_global_thread_num");
1341}
1342
1343OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1344 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1345 ArrayRef<Type *> ResultPtrTys,
1346 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1347 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1348 "expected one result pointer type per in_reduction item");
1349 if (!updateToLocation(Loc))
1350 return Loc.IP;
1351 if (OrigPtrs.empty())
1352 return Builder.saveIP();
1353
1354 // Compute the executing thread's gtid once for the whole target body and
1355 // reuse it for every in_reduction lookup, so a target with several
1356 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1357 // item.
1358 uint32_t SrcLocStrSize;
1359 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1360 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1361 Value *Gtid = getOrCreateThreadID(Ident);
1362
1363 // The runtime entry point takes (and returns) a generic, default-address-
1364 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1365 // taskgroups to find the matching task_reduction registration for the item.
1366 Type *PtrTy = PointerType::getUnqual(M.getContext());
1367 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1368 FunctionCallee GetThData =
1369 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1370
1371 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1372 // Normalize a non-default-address-space original pointer to the generic
1373 // address space before the call.
1374 Value *OrigPtr = OrigPtrs[Idx];
1375 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1376 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1377 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1378
1379 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1380 "omp.inred.priv");
1381
1382 // Cast the returned private pointer back to the requested address space
1383 // when it differs.
1384 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1385 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1386 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1387
1388 MapPrivateCB(Idx, Priv);
1389 }
1390 return Builder.saveIP();
1391}
1392
1395 bool ForceSimpleCall, bool CheckCancelFlag) {
1396 if (!updateToLocation(Loc))
1397 return Loc.IP;
1398
1399 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1400 // __kmpc_barrier(loc, thread_id);
1401
1402 IdentFlag BarrierLocFlags;
1403 switch (Kind) {
1404 case OMPD_for:
1405 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1406 break;
1407 case OMPD_sections:
1408 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1409 break;
1410 case OMPD_single:
1411 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1412 break;
1413 case OMPD_barrier:
1414 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1415 break;
1416 default:
1417 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1418 break;
1419 }
1420
1421 uint32_t SrcLocStrSize;
1422 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1423 Value *Args[] = {
1424 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1425 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1426
1427 // If we are in a cancellable parallel region, barriers are cancellation
1428 // points.
1429 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1430 bool UseCancelBarrier =
1431 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1432
1434 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1435 ? OMPRTL___kmpc_cancel_barrier
1436 : OMPRTL___kmpc_barrier),
1437 Args);
1438
1439 if (UseCancelBarrier && CheckCancelFlag)
1440 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1441 return Err;
1442
1443 return Builder.saveIP();
1444}
1445
1448 Value *IfCondition,
1449 omp::Directive CanceledDirective) {
1450 if (!updateToLocation(Loc))
1451 return Loc.IP;
1452
1453 // LLVM utilities like blocks with terminators.
1454 auto *UI = Builder.CreateUnreachable();
1455
1456 Instruction *ThenTI = UI, *ElseTI = nullptr;
1457 if (IfCondition) {
1458 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1459
1460 // Even if the if condition evaluates to false, this should count as a
1461 // cancellation point
1462 Builder.SetInsertPoint(ElseTI);
1463 auto ElseIP = Builder.saveIP();
1464
1466 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1467 if (!IPOrErr)
1468 return IPOrErr;
1469 }
1470
1471 Builder.SetInsertPoint(ThenTI);
1472
1473 Value *CancelKind = nullptr;
1474 switch (CanceledDirective) {
1475#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1476 case DirectiveEnum: \
1477 CancelKind = Builder.getInt32(Value); \
1478 break;
1479#include "llvm/Frontend/OpenMP/OMPKinds.def"
1480 default:
1481 llvm_unreachable("Unknown cancel kind!");
1482 }
1483
1484 uint32_t SrcLocStrSize;
1485 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1486 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1487 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1489 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1490
1491 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1492 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1493 return Err;
1494
1495 // Update the insertion point and remove the terminator we introduced.
1496 Builder.SetInsertPoint(UI->getParent());
1497 UI->eraseFromParent();
1498
1499 return Builder.saveIP();
1500}
1501
1504 omp::Directive CanceledDirective) {
1505 if (!updateToLocation(Loc))
1506 return Loc.IP;
1507
1508 // LLVM utilities like blocks with terminators.
1509 auto *UI = Builder.CreateUnreachable();
1510 Builder.SetInsertPoint(UI);
1511
1512 Value *CancelKind = nullptr;
1513 switch (CanceledDirective) {
1514#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1515 case DirectiveEnum: \
1516 CancelKind = Builder.getInt32(Value); \
1517 break;
1518#include "llvm/Frontend/OpenMP/OMPKinds.def"
1519 default:
1520 llvm_unreachable("Unknown cancel kind!");
1521 }
1522
1523 uint32_t SrcLocStrSize;
1524 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1525 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1526 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1528 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1529
1530 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1531 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1532 return Err;
1533
1534 // Update the insertion point and remove the terminator we introduced.
1535 Builder.SetInsertPoint(UI->getParent());
1536 UI->eraseFromParent();
1537
1538 return Builder.saveIP();
1539}
1540
1542 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1543 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1544 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1545 if (!updateToLocation(Loc))
1546 return Loc.IP;
1547
1548 Builder.restoreIP(AllocaIP);
1549 auto *KernelArgsPtr =
1550 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1552
1553 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1554 llvm::Value *Arg =
1555 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1556 Builder.CreateAlignedStore(
1557 KernelArgs[I], Arg,
1558 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1559 }
1560
1561 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1562 NumThreads, HostPtr, KernelArgsPtr};
1563
1565 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1566 OffloadingArgs);
1567
1568 return Builder.saveIP();
1569}
1570
1572 const LocationDescription &Loc, Value *OutlinedFnID,
1573 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1574 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1575
1576 if (!updateToLocation(Loc))
1577 return Loc.IP;
1578
1579 // On top of the arrays that were filled up, the target offloading call
1580 // takes as arguments the device id as well as the host pointer. The host
1581 // pointer is used by the runtime library to identify the current target
1582 // region, so it only has to be unique and not necessarily point to
1583 // anything. It could be the pointer to the outlined function that
1584 // implements the target region, but we aren't using that so that the
1585 // compiler doesn't need to keep that, and could therefore inline the host
1586 // function if proven worthwhile during optimization.
1587
1588 // From this point on, we need to have an ID of the target region defined.
1589 assert(OutlinedFnID && "Invalid outlined function ID!");
1590 (void)OutlinedFnID;
1591
1592 // Return value of the runtime offloading call.
1593 Value *Return = nullptr;
1594
1595 // Arguments for the target kernel.
1596 SmallVector<Value *> ArgsVector;
1597 getKernelArgsVector(Args, Builder, ArgsVector);
1598
1599 // The target region is an outlined function launched by the runtime
1600 // via calls to __tgt_target_kernel().
1601 //
1602 // Note that on the host and CPU targets, the runtime implementation of
1603 // these calls simply call the outlined function without forking threads.
1604 // The outlined functions themselves have runtime calls to
1605 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1606 // the compiler in emitTeamsCall() and emitParallelCall().
1607 //
1608 // In contrast, on the NVPTX target, the implementation of
1609 // __tgt_target_teams() launches a GPU kernel with the requested number
1610 // of teams and threads so no additional calls to the runtime are required.
1611 // Check the error code and execute the host version if required.
1612 Builder.restoreIP(emitTargetKernel(
1613 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1614 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1615
1616 BasicBlock *OffloadFailedBlock =
1617 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1618 BasicBlock *OffloadContBlock =
1619 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1620 Value *Failed = Builder.CreateIsNotNull(Return);
1621 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1622
1623 auto CurFn = Builder.GetInsertBlock()->getParent();
1624 emitBlock(OffloadFailedBlock, CurFn);
1625 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1626 if (!AfterIP)
1627 return AfterIP.takeError();
1628 Builder.restoreIP(*AfterIP);
1629 emitBranch(OffloadContBlock);
1630 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1631 return Builder.saveIP();
1632}
1633
1635 Value *CancelFlag, omp::Directive CanceledDirective) {
1636 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1637 "Unexpected cancellation!");
1638
1639 // For a cancel barrier we create two new blocks.
1640 BasicBlock *BB = Builder.GetInsertBlock();
1641 BasicBlock *NonCancellationBlock;
1642 if (Builder.GetInsertPoint() == BB->end()) {
1643 // TODO: This branch will not be needed once we moved to the
1644 // OpenMPIRBuilder codegen completely.
1645 NonCancellationBlock = BasicBlock::Create(
1646 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1647 } else {
1648 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1650 Builder.SetInsertPoint(BB);
1651 }
1652 BasicBlock *CancellationBlock = BasicBlock::Create(
1653 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1654
1655 // Jump to them based on the return value.
1656 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1657 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1658 /* TODO weight */ nullptr, nullptr);
1659
1660 // From the cancellation block we finalize all variables and go to the
1661 // post finalization block that is known to the FiniCB callback.
1662 auto &FI = FinalizationStack.back();
1663 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1664 if (!FiniBBOrErr)
1665 return FiniBBOrErr.takeError();
1666 Builder.SetInsertPoint(CancellationBlock);
1667 Builder.CreateBr(*FiniBBOrErr);
1668
1669 // The continuation block is where code generation continues.
1670 Builder.SetInsertPoint(NonCancellationBlock, NonCancellationBlock->begin());
1671 return Error::success();
1672}
1673
1674/// Create wrapper function used to gather the outlined function's argument
1675/// structure from a shared buffer and to forward them to it when running in
1676/// Generic mode.
1677///
1678/// The outlined function is expected to receive 2 integer arguments followed by
1679/// an optional pointer argument to an argument structure holding the rest.
1681 Function &OutlinedFn) {
1682 size_t NumArgs = OutlinedFn.arg_size();
1683 assert((NumArgs == 2 || NumArgs == 3) &&
1684 "expected a 2-3 argument parallel outlined function");
1685 bool UseArgStruct = NumArgs == 3;
1686
1687 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1688 IRBuilder<>::InsertPointGuard IPG(Builder);
1689 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1690 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1691 /*isVarArg=*/false);
1692 auto *WrapperFn =
1694 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1695
1696 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1697 WrapperFn->addParamAttr(0, Attribute::ZExt);
1698 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1699
1700 BasicBlock *EntryBB =
1701 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1702 Builder.SetInsertPoint(EntryBB);
1703
1704 // Allocation.
1705 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1706 /*ArraySize=*/nullptr, "addr");
1707 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1708 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1709 AddrAlloca->getName() + ".ascast");
1710
1711 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1712 /*ArraySize=*/nullptr, "zero");
1713 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1714 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1715 ZeroAlloca->getName() + ".ascast");
1716
1717 Value *ArgsAlloca = nullptr;
1718 if (UseArgStruct) {
1719 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1720 /*ArraySize=*/nullptr, "global_args");
1721 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1722 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1723 ArgsAlloca->getName() + ".ascast");
1724 }
1725
1726 // Initialization.
1727 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1728 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1729 if (UseArgStruct) {
1730 Builder.CreateCall(
1731 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1732 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1733 {ArgsAlloca});
1734 }
1735
1736 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1737
1738 // Load structArg from global_args.
1739 if (UseArgStruct) {
1740 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1741 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1742 {Builder.getInt64(0)});
1743 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1744 Args.push_back(StructArg);
1745 }
1746
1747 // Call the outlined function holding the parallel body.
1748 Builder.CreateCall(&OutlinedFn, Args);
1749 Builder.CreateRetVoid();
1750
1751 return WrapperFn;
1752}
1753
1754// Callback used to create OpenMP runtime calls to support
1755// omp parallel clause for the device.
1756// We need to use this callback to replace call to the OutlinedFn in OuterFn
1757// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1759 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1760 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1761 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1762 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1763 assert(OutlinedFn.arg_size() >= 2 &&
1764 "Expected at least tid and bounded tid as arguments");
1765 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1766
1767 // Add some known attributes.
1768 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1769 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1770 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1771 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1772 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1773 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1774
1775 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1776 assert(CI && "Expected call instruction to outlined function");
1777 CI->getParent()->setName("omp_parallel");
1778
1779 Builder.SetInsertPoint(CI);
1780 Type *PtrTy = OMPIRBuilder->VoidPtr;
1781
1782 // Add alloca for kernel args
1783 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1784 Builder.SetInsertPoint(OuterAllocaBB, OuterAllocaBB->getFirstInsertionPt());
1785 AllocaInst *ArgsAlloca =
1786 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1787 Value *Args = ArgsAlloca;
1788 // Add address space cast if array for storing arguments is not allocated
1789 // in address space 0
1790 if (ArgsAlloca->getAddressSpace())
1791 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1792 Builder.restoreIP(CurrentIP);
1793
1794 // Store captured vars which are used by kmpc_parallel_60
1795 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1796 Value *V = *(CI->arg_begin() + 2 + Idx);
1797 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1798 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1799 Builder.CreateStore(V, StoreAddress);
1800 }
1801
1802 Value *Cond =
1803 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1804 : Builder.getInt32(1);
1805 Value *NumThreadsArg =
1806 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1807 : Builder.getInt32(-1);
1808
1809 // If this is not a Generic kernel, we can skip generating the wrapper.
1810 Value *WrapperFn;
1811 if (isGenericKernel(*OuterFn))
1812 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1813 else
1814 WrapperFn = Constant::getNullValue(PtrTy);
1815
1816 // Build kmpc_parallel_60 call
1817 Value *Parallel60CallArgs[] = {
1818 /* identifier*/ Ident,
1819 /* global thread num*/ ThreadID,
1820 /* if expression */ Cond,
1821 /* number of threads */ NumThreadsArg,
1822 /* Proc bind */ Builder.getInt32(-1),
1823 /* outlined function */ &OutlinedFn,
1824 /* wrapper function */ WrapperFn,
1825 /* arguments of the outlined funciton*/ Args,
1826 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1827 /* strict for number of threads */ Builder.getInt32(0)};
1828
1829 FunctionCallee RTLFn =
1830 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1831
1832 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1833
1834 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1835 << *Builder.GetInsertBlock()->getParent() << "\n");
1836
1837 // Initialize the local TID stack location with the argument value.
1838 Builder.SetInsertPoint(PrivTID);
1839 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1840 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1841 PrivTIDAddr);
1842
1843 // Remove redundant call to the outlined function.
1844 CI->eraseFromParent();
1845
1846 for (Instruction *I : ToBeDeleted) {
1847 I->eraseFromParent();
1848 }
1849}
1850
1851// Callback used to create OpenMP runtime calls to support
1852// omp parallel clause for the host.
1853// We need to use this callback to replace call to the OutlinedFn in OuterFn
1854// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1855static void
1857 Function *OuterFn, Value *Ident, Value *IfCondition,
1858 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1859 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1860 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1861 FunctionCallee RTLFn;
1862 if (IfCondition) {
1863 RTLFn =
1864 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1865 } else {
1866 RTLFn =
1867 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1868 }
1869 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1870 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1871 LLVMContext &Ctx = F->getContext();
1872 MDBuilder MDB(Ctx);
1873 // Annotate the callback behavior of the __kmpc_fork_call:
1874 // - The callback callee is argument number 2 (microtask).
1875 // - The first two arguments of the callback callee are unknown (-1).
1876 // - All variadic arguments to the __kmpc_fork_call are passed to the
1877 // callback callee.
1878 F->addMetadata(LLVMContext::MD_callback,
1880 2, {-1, -1},
1881 /* VarArgsArePassed */ true)}));
1882 }
1883 }
1884 // Add some known attributes.
1885 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1886 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1887 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1888
1889 assert(OutlinedFn.arg_size() >= 2 &&
1890 "Expected at least tid and bounded tid as arguments");
1891 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1892
1893 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1894 CI->getParent()->setName("omp_parallel");
1895 Builder.SetInsertPoint(CI);
1896
1897 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1898 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1899 &OutlinedFn};
1900
1901 SmallVector<Value *, 16> RealArgs;
1902 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1903 if (IfCondition) {
1904 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1905 RealArgs.push_back(Cond);
1906 }
1907 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1908
1909 // __kmpc_fork_call_if always expects a void ptr as the last argument
1910 // If there are no arguments, pass a null pointer.
1911 auto PtrTy = OMPIRBuilder->VoidPtr;
1912 if (IfCondition && NumCapturedVars == 0) {
1913 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1914 RealArgs.push_back(NullPtrValue);
1915 }
1916
1917 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1918
1919 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1920 << *Builder.GetInsertBlock()->getParent() << "\n");
1921
1922 // Initialize the local TID stack location with the argument value.
1923 Builder.SetInsertPoint(PrivTID);
1924 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1925 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1926 PrivTIDAddr);
1927
1928 // Remove redundant call to the outlined function.
1929 CI->eraseFromParent();
1930
1931 for (Instruction *I : ToBeDeleted) {
1932 I->eraseFromParent();
1933 }
1934}
1935
1937 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1938 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1939 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1940 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1941 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1942
1943 if (!updateToLocation(Loc))
1944 return Loc.IP;
1945
1946 uint32_t SrcLocStrSize;
1947 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1948 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1949 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1950 (ProcBind != OMP_PROC_BIND_default);
1951 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1952 // If we generate code for the target device, we need to allocate
1953 // struct for aggregate params in the device default alloca address space.
1954 // OpenMP runtime requires that the params of the extracted functions are
1955 // passed as zero address space pointers. This flag ensures that extracted
1956 // function arguments are declared in zero address space
1957 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1958
1959 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1960 // only if we compile for host side.
1961 if (NumThreads && !Config.isTargetDevice()) {
1962 Value *Args[] = {
1963 Ident, ThreadID,
1964 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
1966 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
1967 }
1968
1969 if (ProcBind != OMP_PROC_BIND_default) {
1970 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
1971 Value *Args[] = {
1972 Ident, ThreadID,
1973 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
1975 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
1976 }
1977
1978 BasicBlock *InsertBB = Builder.GetInsertBlock();
1979 Function *OuterFn = InsertBB->getParent();
1980
1981 // Save the outer alloca block because the insertion iterator may get
1982 // invalidated and we still need this later.
1983 BasicBlock *OuterAllocaBlock = OuterAllocIP.getBlock();
1984
1985 // Vector to remember instructions we used only during the modeling but which
1986 // we want to delete at the end.
1988
1989 // Change the location to the outer alloca insertion point to create and
1990 // initialize the allocas we pass into the parallel region.
1991 InsertPointTy NewOuter(OuterAllocaBlock, OuterAllocaBlock->begin());
1992 Builder.restoreIP(NewOuter);
1993 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
1994 AllocaInst *ZeroAddrAlloca =
1995 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
1996 Instruction *TIDAddr = TIDAddrAlloca;
1997 Instruction *ZeroAddr = ZeroAddrAlloca;
1998 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
1999 // Add additional casts to enforce pointers in zero address space
2000 TIDAddr = new AddrSpaceCastInst(
2001 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
2002 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
2003 ToBeDeleted.push_back(TIDAddr);
2004 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2005 PointerType ::get(M.getContext(), 0),
2006 "zero.addr.ascast");
2007 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2008 ToBeDeleted.push_back(ZeroAddr);
2009 }
2010
2011 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2012 // associated arguments in the outlined function, so we delete them later.
2013 ToBeDeleted.push_back(TIDAddrAlloca);
2014 ToBeDeleted.push_back(ZeroAddrAlloca);
2015
2016 // Create an artificial insertion point that will also ensure the blocks we
2017 // are about to split are not degenerated.
2018 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2019
2020 BasicBlock *EntryBB = UI->getParent();
2021 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2022 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2023 BasicBlock *PRegPreFiniBB =
2024 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2025 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2026
2027 auto FiniCBWrapper = [&](InsertPointTy IP) {
2028 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2029 // target to the region exit block.
2030 if (IP.getBlock()->end() == IP.getPoint()) {
2032 Builder.restoreIP(IP);
2033 Instruction *I = Builder.CreateBr(PRegExitBB);
2034 IP = InsertPointTy(I->getParent(), I->getIterator());
2035 }
2036 assert(IP.getBlock()->getTerminator()->getNumSuccessors() == 1 &&
2037 IP.getBlock()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2038 "Unexpected insertion point for finalization call!");
2039 return FiniCB(IP);
2040 };
2041
2042 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2043
2044 // Generate the privatization allocas in the block that will become the entry
2045 // of the outlined function.
2046 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2047 InsertPointTy InnerAllocaIP = Builder.saveIP();
2048
2049 AllocaInst *PrivTIDAddr =
2050 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2051 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2052
2053 // Add some fake uses for OpenMP provided arguments.
2054 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2055 Instruction *ZeroAddrUse =
2056 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2057 ToBeDeleted.push_back(ZeroAddrUse);
2058
2059 // EntryBB
2060 // |
2061 // V
2062 // PRegionEntryBB <- Privatization allocas are placed here.
2063 // |
2064 // V
2065 // PRegionBodyBB <- BodeGen is invoked here.
2066 // |
2067 // V
2068 // PRegPreFiniBB <- The block we will start finalization from.
2069 // |
2070 // V
2071 // PRegionExitBB <- A common exit to simplify block collection.
2072 //
2073
2074 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2075
2076 // Let the caller create the body.
2077 assert(BodyGenCB && "Expected body generation callback!");
2078 InsertPointTy CodeGenIP(PRegBodyBB, PRegBodyBB->begin());
2079 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2080 return Err;
2081
2082 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2083
2084 // If OuterFn is a Generic kernel, we need to use device shared memory to
2085 // allocate argument structures. Otherwise, we use stack allocations as usual.
2086 bool UsesDeviceSharedMemory =
2087 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2088 std::unique_ptr<OutlineInfo> OI =
2089 UsesDeviceSharedMemory
2090 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2091 : std::make_unique<OutlineInfo>();
2092
2093 if (Config.isTargetDevice()) {
2094 // Generate OpenMP target specific runtime call
2095 OI->PostOutlineCB = [=, ToBeDeletedVec =
2096 std::move(ToBeDeleted)](Function &OutlinedFn) {
2097 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2098 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2099 ThreadID, ToBeDeletedVec);
2100 };
2101 } else {
2102 // Generate OpenMP host runtime call
2103 OI->PostOutlineCB = [=, ToBeDeletedVec =
2104 std::move(ToBeDeleted)](Function &OutlinedFn) {
2105 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2106 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2107 };
2108 }
2109
2110 OI->FixUpNonEntryAllocas = true;
2111 OI->OuterAllocBB = OuterAllocaBlock;
2112 OI->EntryBB = PRegEntryBB;
2113 OI->ExitBB = PRegExitBB;
2114 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2115 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2116
2117 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2119 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2120
2121 CodeExtractorAnalysisCache CEAC(*OuterFn);
2122 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2123 /* AggregateArgs */ false,
2124 /* BlockFrequencyInfo */ nullptr,
2125 /* BranchProbabilityInfo */ nullptr,
2126 /* AssumptionCache */ nullptr,
2127 /* AllowVarArgs */ true,
2128 /* AllowAlloca */ true,
2129 /* AllocationBlock */ OuterAllocaBlock,
2130 /* DeallocationBlocks */ {},
2131 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2132
2133 // Find inputs to, outputs from the code region.
2134 BasicBlock *CommonExit = nullptr;
2135 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2136 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2137
2138 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2139 /*CollectGlobalInputs=*/true);
2140
2141 Inputs.remove_if([&](Value *I) {
2143 return GV->getValueType() == OpenMPIRBuilder::Ident;
2144
2145 return false;
2146 });
2147
2148 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2149
2150 FunctionCallee TIDRTLFn =
2151 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2152
2153 auto PrivHelper = [&](Value &V) -> Error {
2154 if (&V == TIDAddr || &V == ZeroAddr) {
2155 OI->ExcludeArgsFromAggregate.push_back(&V);
2156 return Error::success();
2157 }
2158
2160 for (Use &U : V.uses())
2161 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2162 if (ParallelRegionBlockSet.count(UserI->getParent()))
2163 Uses.insert(&U);
2164
2165 // __kmpc_fork_call expects extra arguments as pointers. If the input
2166 // already has a pointer type, everything is fine. Otherwise, store the
2167 // value onto stack and load it back inside the to-be-outlined region. This
2168 // will ensure only the pointer will be passed to the function.
2169 // FIXME: if there are more than 15 trailing arguments, they must be
2170 // additionally packed in a struct.
2171 Value *Inner = &V;
2172 if (!V.getType()->isPointerTy()) {
2174 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2175
2176 Builder.restoreIP(OuterAllocIP);
2177 Value *Ptr;
2178 if (UsesDeviceSharedMemory) {
2179 // Use device shared memory instead, if needed.
2180 Ptr = createOMPAllocShared(OuterAllocIP, V.getType(),
2181 V.getName() + ".reloaded");
2182 for (BasicBlock *DeallocBlock : OuterDeallocBlocks)
2184 InsertPointTy(DeallocBlock, DeallocBlock->getFirstInsertionPt()),
2185 Ptr, V.getType());
2186 } else {
2187 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2188 V.getName() + ".reloaded");
2189 }
2190
2191 // Store to stack at end of the block that currently branches to the entry
2192 // block of the to-be-outlined region.
2193 Builder.SetInsertPoint(InsertBB,
2194 InsertBB->getTerminator()->getIterator());
2195 Builder.CreateStore(&V, Ptr);
2196
2197 // Load back next to allocations in the to-be-outlined region.
2198 Builder.restoreIP(InnerAllocaIP);
2199 Inner = Builder.CreateLoad(V.getType(), Ptr);
2200 }
2201
2202 Value *ReplacementValue = nullptr;
2203 CallInst *CI = dyn_cast<CallInst>(&V);
2204 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2205 ReplacementValue = PrivTID;
2206 } else {
2207 InsertPointOrErrorTy AfterIP =
2208 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2209 if (!AfterIP)
2210 return AfterIP.takeError();
2211 Builder.restoreIP(*AfterIP);
2212 InnerAllocaIP = {
2213 InnerAllocaIP.getBlock(),
2214 InnerAllocaIP.getBlock()->getTerminator()->getIterator()};
2215
2216 assert(ReplacementValue &&
2217 "Expected copy/create callback to set replacement value!");
2218 if (ReplacementValue == &V)
2219 return Error::success();
2220 }
2221
2222 for (Use *UPtr : Uses)
2223 UPtr->set(ReplacementValue);
2224
2225 return Error::success();
2226 };
2227
2228 // Reset the inner alloca insertion as it will be used for loading the values
2229 // wrapped into pointers before passing them into the to-be-outlined region.
2230 // Configure it to insert immediately after the fake use of zero address so
2231 // that they are available in the generated body and so that the
2232 // OpenMP-related values (thread ID and zero address pointers) remain leading
2233 // in the argument list.
2234 InnerAllocaIP = IRBuilder<>::InsertPoint(
2235 ZeroAddrUse->getParent(), ZeroAddrUse->getNextNode()->getIterator());
2236
2237 // Reset the outer alloca insertion point to the entry of the relevant block
2238 // in case it was invalidated.
2239 OuterAllocIP = IRBuilder<>::InsertPoint(
2240 OuterAllocaBlock, OuterAllocaBlock->getFirstInsertionPt());
2241
2242 for (Value *Input : Inputs) {
2243 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2244 if (Error Err = PrivHelper(*Input))
2245 return Err;
2246 }
2247 LLVM_DEBUG({
2248 for (Value *Output : Outputs)
2249 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2250 });
2251 assert(Outputs.empty() &&
2252 "OpenMP outlining should not produce live-out values!");
2253
2254 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2255 LLVM_DEBUG({
2256 for (auto *BB : Blocks)
2257 dbgs() << " PBR: " << BB->getName() << "\n";
2258 });
2259
2260 // Adjust the finalization stack, verify the adjustment, and call the
2261 // finalize function a last time to finalize values between the pre-fini
2262 // block and the exit block if we left the parallel "the normal way".
2263 auto FiniInfo = FinalizationStack.pop_back_val();
2264 (void)FiniInfo;
2265 assert(FiniInfo.DK == OMPD_parallel &&
2266 "Unexpected finalization stack state!");
2267
2268 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2269
2270 InsertPointTy PreFiniIP(PRegPreFiniBB, PRegPreFiniTI->getIterator());
2271 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2272 if (!FiniBBOrErr)
2273 return FiniBBOrErr.takeError();
2274 {
2276 Builder.restoreIP(PreFiniIP);
2277 Builder.CreateBr(*FiniBBOrErr);
2278 // There's currently a branch to omp.par.exit. Delete it. We will get there
2279 // via the fini block
2280 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2281 Term->eraseFromParent();
2282 }
2283
2284 // Register the outlined info.
2285 addOutlineInfo(std::move(OI));
2286
2287 InsertPointTy AfterIP(UI->getParent(), UI->getParent()->end());
2288 UI->eraseFromParent();
2289
2290 return AfterIP;
2291}
2292
2294 // Build call void __kmpc_flush(ident_t *loc)
2295 uint32_t SrcLocStrSize;
2296 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2297 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2298
2300 Args);
2301}
2302
2304 if (!updateToLocation(Loc))
2305 return;
2306 emitFlush(Loc);
2307}
2308
2310 Value *Message) {
2311 if (!updateToLocation(Loc))
2312 return;
2313
2314 // Build call void __kmpc_error(ident_t *loc, int severity,
2315 // const char *message)
2316 uint32_t SrcLocStrSize;
2317 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2318 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2319 // Severity: 1 = warning, 2 = fatal.
2320 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2321 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2322 Value *Args[] = {Ident, Severity, MessageArg};
2323
2325 Args);
2326}
2327
2329 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2330 uint32_t SrcLocStrSize;
2331 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2332 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2333 Constant *I32Null = ConstantInt::getNullValue(Int32);
2334 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2335
2337 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2338}
2339
2345
2347 const DependData &Dep) {
2348 // Store the pointer to the variable
2349 Value *Addr = Builder.CreateStructGEP(
2350 DependInfo, Entry,
2351 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2352 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2353 Builder.CreateStore(DepValPtr, Addr);
2354 // Store the size of the variable
2355 Value *Size = Builder.CreateStructGEP(
2356 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2357 Builder.CreateStore(
2358 ConstantInt::get(SizeTy,
2359 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2360 Size);
2361 // Store the dependency kind
2362 Value *Flags = Builder.CreateStructGEP(
2363 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2364 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2365 static_cast<unsigned int>(Dep.DepKind)),
2366 Flags);
2367}
2368
2369// Processes the dependencies in Dependencies and does the following
2370// - Allocates space on the stack of an array of DependInfo objects
2371// - Populates each DependInfo object with relevant information of
2372// the corresponding dependence.
2373// - All code is inserted in the entry block of the current function.
2375 OpenMPIRBuilder &OMPBuilder,
2377 // Early return if we have no dependencies to process
2378 if (Dependencies.empty())
2379 return nullptr;
2380
2381 // Given a vector of DependData objects, in this function we create an
2382 // array on the stack that holds kmp_depend_info objects corresponding
2383 // to each dependency. This is then passed to the OpenMP runtime.
2384 // For example, if there are 'n' dependencies then the following psedo
2385 // code is generated. Assume the first dependence is on a variable 'a'
2386 //
2387 // \code{c}
2388 // DepArray = alloc(n x sizeof(kmp_depend_info);
2389 // idx = 0;
2390 // DepArray[idx].base_addr = ptrtoint(&a);
2391 // DepArray[idx].len = 8;
2392 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2393 // ++idx;
2394 // DepArray[idx].base_addr = ...;
2395 // \endcode
2396
2397 IRBuilderBase &Builder = OMPBuilder.Builder;
2398 Type *DependInfo = OMPBuilder.DependInfo;
2399
2400 Value *DepArray = nullptr;
2401 OpenMPIRBuilder::InsertPointTy OldIP = Builder.saveIP();
2402 Builder.SetInsertPoint(
2404
2405 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2406 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2407
2408 Builder.restoreIP(OldIP);
2409
2410 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2411 Value *Base =
2412 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2413 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2414 }
2415 return DepArray;
2416}
2417
2419 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2420 // global_tid);
2421 uint32_t SrcLocStrSize;
2422 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2423 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2424 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2425
2426 // Ignore return result until untied tasks are supported.
2428 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2429}
2430
2432 DependenciesInfo Dependencies) {
2433 if (!updateToLocation(Loc))
2434 return;
2435
2436 Value *DepArray = nullptr;
2437 Type *DepArrayTy = nullptr;
2438 Value *NumDeps = nullptr;
2439 if (Dependencies.DepArray) {
2440 DepArray = Dependencies.DepArray;
2441 NumDeps = Dependencies.NumDeps;
2442 } else if (!Dependencies.Deps.empty()) {
2443 InsertPointTy OldIP = Builder.saveIP();
2444 BasicBlock &entryBB =
2445 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2446 Builder.SetInsertPoint(&entryBB, entryBB.getFirstInsertionPt());
2447
2448 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2449 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2450 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2451
2452 Builder.restoreIP(OldIP);
2453 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2454 Value *Base =
2455 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2456 this->emitTaskDependency(Builder, Base, Dep);
2457 }
2458 }
2459
2460 if (DepArray) {
2461 uint32_t SrcLocStrSize;
2462 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2463 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2464 Value *Args[] = {
2465 Ident,
2466 getOrCreateThreadID(Ident),
2467 NumDeps,
2468 DepArray,
2469 ConstantInt::get(Builder.getInt32Ty(), 0),
2471 ConstantInt::get(Builder.getInt32Ty(), false)};
2474 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2475 Args);
2476 } else {
2478 }
2479}
2480
2481/// Create the task duplication function passed to kmpc_taskloop.
2482Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2483 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2484 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2485 if (!DupCB)
2487 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2488
2489 // From OpenMP Runtime p_task_dup_t:
2490 // Routine optionally generated by the compiler for setting the lastprivate
2491 // flag and calling needed constructors for private/firstprivate objects (used
2492 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2493 // lastprivate flag.
2494 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2495
2496 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2497
2498 FunctionType *DupFuncTy = FunctionType::get(
2499 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2500 /*isVarArg=*/false);
2501
2502 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2503 "omp_taskloop_dup", M);
2504 Value *DestTaskArg = DupFunction->getArg(0);
2505 Value *SrcTaskArg = DupFunction->getArg(1);
2506 Value *LastprivateFlagArg = DupFunction->getArg(2);
2507 DestTaskArg->setName("dest_task");
2508 SrcTaskArg->setName("src_task");
2509 LastprivateFlagArg->setName("lastprivate_flag");
2510
2511 IRBuilderBase::InsertPointGuard Guard(Builder);
2512 Builder.SetInsertPoint(
2513 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2514
2515 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2516 Type *TaskWithPrivatesTy =
2517 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2518 Value *TaskPrivates = Builder.CreateGEP(
2519 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2520 Value *ContextPtr = Builder.CreateGEP(
2521 PrivatesTy, TaskPrivates,
2522 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2523 return ContextPtr;
2524 };
2525
2526 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2527 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2528
2529 DestTaskContextPtr->setName("destPtr");
2530 SrcTaskContextPtr->setName("srcPtr");
2531
2532 InsertPointTy AllocaIP(&DupFunction->getEntryBlock(),
2533 DupFunction->getEntryBlock().begin());
2534 InsertPointTy CodeGenIP = Builder.saveIP();
2535 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2536 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2537 if (!AfterIPOrError)
2538 return AfterIPOrError.takeError();
2539 Builder.restoreIP(*AfterIPOrError);
2540
2541 Builder.CreateRetVoid();
2542
2543 return DupFunction;
2544}
2545
2546OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2547 const LocationDescription &Loc, InsertPointTy AllocaIP,
2548 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2549 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2550 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2551 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2552 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2553 Value *TaskContextStructPtrVal) {
2554
2555 if (!updateToLocation(Loc))
2556 return InsertPointTy();
2557
2558 uint32_t SrcLocStrSize;
2559 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2560 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2561
2562 BasicBlock *TaskloopExitBB =
2563 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2564 BasicBlock *TaskloopBodyBB =
2565 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2566 BasicBlock *TaskloopAllocaBB =
2567 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2568
2569 InsertPointTy TaskloopAllocaIP =
2570 InsertPointTy(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2571 InsertPointTy TaskloopBodyIP =
2572 InsertPointTy(TaskloopBodyBB, TaskloopBodyBB->begin());
2573
2574 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2575 return Err;
2576
2577 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2578 if (!result) {
2579 return result.takeError();
2580 }
2581
2582 llvm::CanonicalLoopInfo *CLI = result.get();
2583 auto OI = std::make_unique<OutlineInfo>();
2584 OI->EntryBB = TaskloopAllocaBB;
2585 OI->OuterAllocBB = AllocaIP.getBlock();
2586 OI->ExitBB = TaskloopExitBB;
2587 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2588 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2589
2590 // Add the thread ID argument.
2591 SmallVector<Instruction *> ToBeDeleted;
2592 // dummy instruction to be used as a fake argument
2593 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2594 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2595 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2596 TaskloopAllocaIP, "lb", false, true);
2597 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2598 TaskloopAllocaIP, "ub", false, true);
2599 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2600 TaskloopAllocaIP, "step", false, true);
2601 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2602 // aggregate struct
2603 OI->Inputs.insert(FakeLB);
2604 OI->Inputs.insert(FakeUB);
2605 OI->Inputs.insert(FakeStep);
2606 if (TaskContextStructPtrVal)
2607 OI->Inputs.insert(TaskContextStructPtrVal);
2608 assert(((TaskContextStructPtrVal && DupCB) ||
2609 (!TaskContextStructPtrVal && !DupCB)) &&
2610 "Task context struct ptr and duplication callback must be both set "
2611 "or both null");
2612
2613 // It isn't safe to run the duplication bodygen callback inside the post
2614 // outlining callback so this has to be run now before we know the real task
2615 // shareds structure type.
2616 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2617 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2618 Type *FakeSharedsTy = StructType::get(
2619 Builder.getContext(),
2620 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2621 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2622 FakeSharedsTy,
2623 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2624 if (!TaskDupFnOrErr) {
2625 return TaskDupFnOrErr.takeError();
2626 }
2627 Value *TaskDupFn = *TaskDupFnOrErr;
2628
2629 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2630 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2631 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2632 FakeSharedsTy, Final, Mergeable, Priority,
2633 NumOfCollapseLoops](Function &OutlinedFn) mutable {
2634 // Replace the Stale CI by appropriate RTL function call.
2635 assert(OutlinedFn.hasOneUse() &&
2636 "there must be a single user for the outlined function");
2637 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2638
2639 /* Create the casting for the Bounds Values that can be used when outlining
2640 * to replace the uses of the fakes with real values */
2641 BasicBlock *CodeReplBB = StaleCI->getParent();
2642 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2643 Value *CastedLBVal =
2644 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2645 Value *CastedUBVal =
2646 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2647 Value *CastedStepVal =
2648 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2649
2650 Builder.SetInsertPoint(StaleCI);
2651
2652 // Gather the arguments for emitting the runtime call for
2653 // @__kmpc_omp_task_alloc
2654 Function *TaskAllocFn =
2655 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2656
2657 Value *ThreadID = getOrCreateThreadID(Ident);
2658
2659 if (!NoGroup) {
2660 // Emit runtime call for @__kmpc_taskgroup
2661 Function *TaskgroupFn =
2662 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2663 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2664 }
2665
2666 // `flags` Argument Configuration
2667 // Task is tied if (Flags & 1) == 1.
2668 // Task is untied if (Flags & 1) == 0.
2669 // Task is final if (Flags & 2) == 2.
2670 // Task is not final if (Flags & 2) == 0.
2671 // Task is mergeable if (Flags & 4) == 4.
2672 // Task is not mergeable if (Flags & 4) == 0.
2673 // Task is priority if (Flags & 32) == 32.
2674 // Task is not priority if (Flags & 32) == 0.
2675 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2676 if (Final)
2677 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2678 if (Mergeable)
2679 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2680 if (Priority)
2681 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2682
2683 Value *TaskSize = Builder.getInt64(
2684 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2685
2686 AllocaInst *ArgStructAlloca =
2688 assert(ArgStructAlloca &&
2689 "Unable to find the alloca instruction corresponding to arguments "
2690 "for extracted function");
2691 std::optional<TypeSize> ArgAllocSize =
2692 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2693 assert(ArgAllocSize &&
2694 "Unable to determine size of arguments for extracted function");
2695 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2696
2697 // Emit the @__kmpc_omp_task_alloc runtime call
2698 // The runtime call returns a pointer to an area where the task captured
2699 // variables must be copied before the task is run (TaskData)
2700 CallInst *TaskData = Builder.CreateCall(
2701 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2702 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2703 /*task_func=*/&OutlinedFn});
2704
2705 Value *Shareds = StaleCI->getArgOperand(1);
2706 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2707 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2708 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2709 SharedsSize);
2710 // Get the pointer to loop lb, ub, step from task ptr
2711 // and set up the lowerbound,upperbound and step values
2712 llvm::Value *Lb = Builder.CreateGEP(
2713 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2714
2715 llvm::Value *Ub = Builder.CreateGEP(
2716 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2717
2718 llvm::Value *Step = Builder.CreateGEP(
2719 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2720 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2721
2722 // set up the arguments for emitting kmpc_taskloop runtime call
2723 // setting values for ifval, nogroup, sched, grainsize, task_dup
2724 Value *IfCondVal =
2725 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2726 : Builder.getInt32(1);
2727 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2728 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2729 Value *NoGroupVal = Builder.getInt32(1);
2730 Value *SchedVal = Builder.getInt32(Sched);
2731 Value *GrainSizeVal =
2732 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2733 : Builder.getInt64(0);
2734 Value *TaskDup = TaskDupFn;
2735
2736 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2737 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2738
2739 // taskloop runtime call
2740 Function *TaskloopFn =
2741 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2742 Builder.CreateCall(TaskloopFn, Args);
2743
2744 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2745 // nogroup is not defined
2746 if (!NoGroup) {
2747 Function *EndTaskgroupFn =
2748 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2749 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2750 }
2751
2752 StaleCI->eraseFromParent();
2753
2754 Builder.SetInsertPoint(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2755
2756 LoadInst *SharedsOutlined =
2757 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2758 OutlinedFn.getArg(1)->replaceUsesWithIf(
2759 SharedsOutlined,
2760 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2761
2762 Value *IV = CLI->getIndVar();
2763 Type *IVTy = IV->getType();
2764 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2765
2766 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2767 // UpperBound. These GEP's can be reused for loading the tasks respective
2768 // bounds.
2769 Value *TaskLB = nullptr;
2770 Value *TaskUB = nullptr;
2771 Value *TaskStep = nullptr;
2772 Value *LoadTaskLB = nullptr;
2773 Value *LoadTaskUB = nullptr;
2774 Value *LoadTaskStep = nullptr;
2775 for (Instruction &I : *TaskloopAllocaBB) {
2776 if (I.getOpcode() == Instruction::GetElementPtr) {
2777 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2778 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2779 switch (CI->getZExtValue()) {
2780 case 0:
2781 TaskLB = &I;
2782 break;
2783 case 1:
2784 TaskUB = &I;
2785 break;
2786 case 2:
2787 TaskStep = &I;
2788 break;
2789 }
2790 }
2791 } else if (I.getOpcode() == Instruction::Load) {
2792 LoadInst &Load = cast<LoadInst>(I);
2793 if (Load.getPointerOperand() == TaskLB) {
2794 assert(TaskLB != nullptr && "Expected value for TaskLB");
2795 LoadTaskLB = &I;
2796 } else if (Load.getPointerOperand() == TaskUB) {
2797 assert(TaskUB != nullptr && "Expected value for TaskUB");
2798 LoadTaskUB = &I;
2799 } else if (Load.getPointerOperand() == TaskStep) {
2800 assert(TaskStep != nullptr && "Expected value for TaskStep");
2801 LoadTaskStep = &I;
2802 }
2803 }
2804 }
2805
2806 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2807
2808 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2809 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2810 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2811 Value *TripCountMinusOne = Builder.CreateSDiv(
2812 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2813 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2814 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2815 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2816 // set the trip count in the CLI
2817 CLI->setTripCount(CastedTripCount);
2818
2819 Builder.SetInsertPoint(CLI->getBody(),
2820 CLI->getBody()->getFirstInsertionPt());
2821
2822 if (NumOfCollapseLoops > 1) {
2823 llvm::SmallVector<User *> UsersToReplace;
2824 // When using the collapse clause, the bounds of the loop have to be
2825 // adjusted to properly represent the iterator of the outer loop.
2826 Value *IVPlusTaskLB = Builder.CreateAdd(
2827 CLI->getIndVar(),
2828 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2829 // To ensure every Use is correctly captured, we first want to record
2830 // which users to replace the value in, and then replace the value.
2831 for (auto IVUse = CLI->getIndVar()->uses().begin();
2832 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2833 User *IVUser = IVUse->getUser();
2834 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2835 if (Op->getOpcode() == Instruction::URem ||
2836 Op->getOpcode() == Instruction::UDiv) {
2837 UsersToReplace.push_back(IVUser);
2838 }
2839 }
2840 }
2841 for (User *User : UsersToReplace) {
2842 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2843 }
2844 } else {
2845 // The canonical loop is generated with a fixed lower bound. We need to
2846 // update the index calculation code to use the task's lower bound. The
2847 // generated code looks like this:
2848 // %omp_loop.iv = phi ...
2849 // ...
2850 // %tmp = mul [type] %omp_loop.iv, step
2851 // %user_index = add [type] tmp, lb
2852 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2853 // of the normalised induction variable:
2854 // 1. This one: converting the normalised IV to the user IV
2855 // 2. The increment (add)
2856 // 3. The comparison against the trip count (icmp)
2857 // (1) is the only use that is a mul followed by an add so this cannot
2858 // match other IR.
2859 assert(CLI->getIndVar()->getNumUses() == 3 &&
2860 "Canonical loop should have exactly three uses of the ind var");
2861 for (User *IVUser : CLI->getIndVar()->users()) {
2862 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2863 if (Mul->getOpcode() == Instruction::Mul) {
2864 for (User *MulUser : Mul->users()) {
2865 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2866 if (Add->getOpcode() == Instruction::Add) {
2867 Add->setOperand(1, CastedTaskLB);
2868 }
2869 }
2870 }
2871 }
2872 }
2873 }
2874 }
2875
2876 FakeLB->replaceAllUsesWith(CastedLBVal);
2877 FakeUB->replaceAllUsesWith(CastedUBVal);
2878 FakeStep->replaceAllUsesWith(CastedStepVal);
2879 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2880 I->eraseFromParent();
2881 }
2882 };
2883
2884 addOutlineInfo(std::move(OI));
2885 Builder.SetInsertPoint(TaskloopExitBB, TaskloopExitBB->begin());
2886 return Builder.saveIP();
2887}
2888
2891 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2893 llvm::Type::getInt32Ty(M.getContext()));
2894}
2895
2897 const LocationDescription &Loc, InsertPointTy AllocaIP,
2898 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2899 bool Tied, Value *Final, Value *IfCondition,
2900 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2901 bool Mergeable, Value *EventHandle, Value *Priority) {
2902
2903 if (!updateToLocation(Loc))
2904 return InsertPointTy();
2905
2906 uint32_t SrcLocStrSize;
2907 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2908 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2909 // The current basic block is split into four basic blocks. After outlining,
2910 // they will be mapped as follows:
2911 // ```
2912 // def current_fn() {
2913 // current_basic_block:
2914 // br label %task.exit
2915 // task.exit:
2916 // ; instructions after task
2917 // }
2918 // def outlined_fn() {
2919 // task.alloca:
2920 // br label %task.body
2921 // task.body:
2922 // ret void
2923 // }
2924 // ```
2925 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2926 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2927 BasicBlock *TaskAllocaBB =
2928 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2929
2930 InsertPointTy TaskAllocaIP =
2931 InsertPointTy(TaskAllocaBB, TaskAllocaBB->begin());
2932 InsertPointTy TaskBodyIP = InsertPointTy(TaskBodyBB, TaskBodyBB->begin());
2933 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2934 return Err;
2935
2936 auto OI = std::make_unique<OutlineInfo>();
2937 OI->EntryBB = TaskAllocaBB;
2938 OI->OuterAllocBB = AllocaIP.getBlock();
2939 OI->ExitBB = TaskExitBB;
2940 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2941 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2942
2943 // Add the thread ID argument.
2945 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2946 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2947
2948 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2949 Affinities, Mergeable, Priority, EventHandle,
2950 TaskAllocaBB,
2951 ToBeDeleted](Function &OutlinedFn) mutable {
2952 // Replace the Stale CI by appropriate RTL function call.
2953 assert(OutlinedFn.hasOneUse() &&
2954 "there must be a single user for the outlined function");
2955 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2956
2957 // HasShareds is true if any variables are captured in the outlined region,
2958 // false otherwise.
2959 bool HasShareds = StaleCI->arg_size() > 1;
2960 Builder.SetInsertPoint(StaleCI);
2961
2962 // Gather the arguments for emitting the runtime call for
2963 // @__kmpc_omp_task_alloc
2964 Function *TaskAllocFn =
2965 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2966
2967 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
2968 // call.
2969 Value *ThreadID = getOrCreateThreadID(Ident);
2970
2971 // Argument - `flags`
2972 // Task is tied iff (Flags & 1) == 1.
2973 // Task is untied iff (Flags & 1) == 0.
2974 // Task is final iff (Flags & 2) == 2.
2975 // Task is not final iff (Flags & 2) == 0.
2976 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
2977 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
2978 // Task is detachable iff (Flags & 64) == 64.
2979 // Task is not detachable iff (Flags & 64) == 0.
2980 // Task is priority iff (Flags & 32) == 32.
2981 // Task is not priority iff (Flags & 32) == 0.
2982 // TODO: Handle the other flags.
2983 Value *Flags = Builder.getInt32(Tied);
2984 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
2985 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
2986 if (Final) {
2987 Value *FinalFlag =
2988 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
2989 Flags = Builder.CreateOr(FinalFlag, Flags);
2990 }
2991
2992 if (Mergeable || UseMergedIf0Path)
2993 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2994 if (EventHandle)
2995 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
2996 if (Priority)
2997 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2998
2999 // Argument - `sizeof_kmp_task_t` (TaskSize)
3000 // Tasksize refers to the size in bytes of kmp_task_t data structure
3001 // including private vars accessed in task.
3002 // TODO: add kmp_task_t_with_privates (privates)
3003 Value *TaskSize = Builder.getInt64(
3004 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3005
3006 // Argument - `sizeof_shareds` (SharedsSize)
3007 // SharedsSize refers to the shareds array size in the kmp_task_t data
3008 // structure.
3009 Value *SharedsSize = Builder.getInt64(0);
3010 if (HasShareds) {
3011 AllocaInst *ArgStructAlloca =
3013 assert(ArgStructAlloca &&
3014 "Unable to find the alloca instruction corresponding to arguments "
3015 "for extracted function");
3016 std::optional<TypeSize> ArgAllocSize =
3017 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3018 assert(ArgAllocSize &&
3019 "Unable to determine size of arguments for extracted function");
3020 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3021 }
3022 // Emit the @__kmpc_omp_task_alloc runtime call
3023 // The runtime call returns a pointer to an area where the task captured
3024 // variables must be copied before the task is run (TaskData)
3026 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3027 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3028 /*task_func=*/&OutlinedFn});
3029
3030 if (Affinities.Count && Affinities.Info) {
3032 OMPRTL___kmpc_omp_reg_task_with_affinity);
3033
3034 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3035 Affinities.Count, Affinities.Info});
3036 }
3037
3038 // Emit detach clause initialization.
3039 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3040 // task_descriptor);
3041 if (EventHandle) {
3043 OMPRTL___kmpc_task_allow_completion_event);
3044 llvm::Value *EventVal =
3045 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3046 llvm::Value *EventHandleAddr =
3047 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3048 Builder.getPtrTy(0));
3049 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3050 Builder.CreateStore(EventVal, EventHandleAddr);
3051 }
3052 // Copy the arguments for outlined function
3053 if (HasShareds) {
3054 Value *Shareds = StaleCI->getArgOperand(1);
3055 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3056 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3057 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3058 SharedsSize);
3059 }
3060
3061 if (Priority) {
3062 //
3063 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3064 // we populate the priority information into the "kmp_task_t" here
3065 //
3066 // The struct "kmp_task_t" definition is available in kmp.h
3067 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3068 // data2 is used for priority
3069 //
3070 Type *Int32Ty = Builder.getInt32Ty();
3071 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3072 // kmp_task_t* => { ptr }
3073 Type *TaskPtr = StructType::get(VoidPtr);
3074 Value *TaskGEP =
3075 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3076 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3077 Type *TaskStructType = StructType::get(
3078 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3079 Value *PriorityData = Builder.CreateInBoundsGEP(
3080 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3081 // kmp_cmplrdata_t => { ptr, ptr }
3082 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3083 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3084 PriorityData, {Zero, Zero});
3085 Builder.CreateStore(Priority, CmplrData);
3086 }
3087
3088 Value *DepArray = nullptr;
3089 Value *NumDeps = nullptr;
3090 if (Dependencies.DepArray) {
3091 DepArray = Dependencies.DepArray;
3092 NumDeps = Dependencies.NumDeps;
3093 } else if (!Dependencies.Deps.empty()) {
3094 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3095 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3096 }
3097
3098 // In the presence of the `if` clause, the following IR is generated:
3099 // ...
3100 // %data = call @__kmpc_omp_task_alloc(...)
3101 // br i1 %if_condition, label %then, label %else
3102 // then:
3103 // call @__kmpc_omp_task(...)
3104 // br label %exit
3105 // else:
3106 // ;; Wait for resolution of dependencies, if any, before
3107 // ;; beginning the task
3108 // call @__kmpc_omp_wait_deps(...)
3109 // call @__kmpc_omp_task_begin_if0(...)
3110 // call @outlined_fn(...)
3111 // call @__kmpc_omp_task_complete_if0(...)
3112 // br label %exit
3113 // exit:
3114 // ...
3115 if (IfCondition && !UseMergedIf0Path) {
3116 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3117 // terminator.
3118 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3119 Instruction *IfTerminator =
3120 Builder.GetInsertPoint()->getParent()->getTerminator();
3121 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3122 Builder.SetInsertPoint(IfTerminator);
3123 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3124 &ElseTI);
3125 Builder.SetInsertPoint(ElseTI);
3126
3127 if (DepArray) {
3128 Function *TaskWaitFn =
3129 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3131 TaskWaitFn,
3132 {Ident, ThreadID, NumDeps, DepArray,
3133 ConstantInt::get(Builder.getInt32Ty(), 0),
3135 }
3136 Function *TaskBeginFn =
3137 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3138 Function *TaskCompleteFn =
3139 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3140 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3141 CallInst *CI = nullptr;
3142 if (HasShareds)
3143 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3144 else
3145 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3146 CI->setDebugLoc(StaleCI->getDebugLoc());
3147 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3148 Builder.SetInsertPoint(ThenTI);
3149 }
3150
3151 if (DepArray) {
3152 Function *TaskFn =
3153 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3155 TaskFn,
3156 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3157 ConstantInt::get(Builder.getInt32Ty(), 0),
3159
3160 } else {
3161 // Emit the @__kmpc_omp_task runtime call to spawn the task
3162 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3163 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3164 }
3165
3166 StaleCI->eraseFromParent();
3167
3168 Builder.SetInsertPoint(TaskAllocaBB, TaskAllocaBB->begin());
3169 if (HasShareds) {
3170 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3171 OutlinedFn.getArg(1)->replaceUsesWithIf(
3172 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3173 }
3174
3175 // The insert point may refer to one of the instructions about to be
3176 // deleted. It is not needed anymore so clear it instead of leaving it
3177 // dangling.
3178 Builder.ClearInsertionPoint();
3179 for (Instruction *I : llvm::reverse(ToBeDeleted))
3180 I->eraseFromParent();
3181 };
3182
3183 addOutlineInfo(std::move(OI));
3184 Builder.SetInsertPoint(TaskExitBB, TaskExitBB->begin());
3185
3186 return Builder.saveIP();
3187}
3188
3190 const LocationDescription &Loc, InsertPointTy AllocaIP,
3191 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3192 if (!updateToLocation(Loc))
3193 return InsertPointTy();
3194
3195 uint32_t SrcLocStrSize;
3196 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3197 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3198 Value *ThreadID = getOrCreateThreadID(Ident);
3199
3200 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3201 Function *TaskgroupFn =
3202 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3203 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3204
3205 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3206 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3207 return Err;
3208
3209 Builder.SetInsertPoint(TaskgroupExitBB);
3210 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3211 Function *EndTaskgroupFn =
3212 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3213 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3214
3215 return Builder.saveIP();
3216}
3217
3219 const LocationDescription &Loc, InsertPointTy AllocaIP,
3221 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3222 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3223
3224 if (!updateToLocation(Loc))
3225 return Loc.IP;
3226
3227 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3228
3229 // Each section is emitted as a switch case
3230 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3231 // -> OMP.createSection() which generates the IR for each section
3232 // Iterate through all sections and emit a switch construct:
3233 // switch (IV) {
3234 // case 0:
3235 // <SectionStmt[0]>;
3236 // break;
3237 // ...
3238 // case <NumSection> - 1:
3239 // <SectionStmt[<NumSection> - 1]>;
3240 // break;
3241 // }
3242 // ...
3243 // section_loop.after:
3244 // <FiniCB>;
3245 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3246 Builder.restoreIP(CodeGenIP);
3248 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3249 Function *CurFn = Continue->getParent();
3250 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3251
3252 unsigned CaseNumber = 0;
3253 for (auto SectionCB : SectionCBs) {
3255 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3256 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3257 Builder.SetInsertPoint(CaseBB);
3258 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3259 if (Error Err =
3260 SectionCB(InsertPointTy(),
3261 {CaseEndBr->getParent(), CaseEndBr->getIterator()}, {}))
3262 return Err;
3263 CaseNumber++;
3264 }
3265 // remove the existing terminator from body BB since there can be no
3266 // terminators after switch/case
3267 return Error::success();
3268 };
3269 // Loop body ends here
3270 // LowerBound, UpperBound, and STride for createCanonicalLoop
3271 Type *I32Ty = Type::getInt32Ty(M.getContext());
3272 Value *LB = ConstantInt::get(I32Ty, 0);
3273 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3274 Value *ST = ConstantInt::get(I32Ty, 1);
3276 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3277 if (!LoopInfo)
3278 return LoopInfo.takeError();
3279
3280 InsertPointOrErrorTy WsloopIP =
3281 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3282 WorksharingLoopType::ForStaticLoop, !IsNowait);
3283 if (!WsloopIP)
3284 return WsloopIP.takeError();
3285 InsertPointTy AfterIP = *WsloopIP;
3286
3287 BasicBlock *LoopFini = AfterIP.getBlock()->getSinglePredecessor();
3288 assert(LoopFini && "Bad structure of static workshare loop finalization");
3289
3290 // Apply the finalization callback in LoopAfterBB
3291 auto FiniInfo = FinalizationStack.pop_back_val();
3292 assert(FiniInfo.DK == OMPD_sections &&
3293 "Unexpected finalization stack state!");
3294 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3295 return Err;
3296
3297 return AfterIP;
3298}
3299
3302 BodyGenCallbackTy BodyGenCB,
3303 FinalizeCallbackTy FiniCB) {
3304 if (!updateToLocation(Loc))
3305 return Loc.IP;
3306
3307 auto FiniCBWrapper = [&](InsertPointTy IP) {
3308 if (IP.getBlock()->end() != IP.getPoint())
3309 return FiniCB(IP);
3310 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3311 // will fail because that function requires the Finalization Basic Block to
3312 // have a terminator, which is already removed by EmitOMPRegionBody.
3313 // IP is currently at cancelation block.
3314 // We need to backtrack to the condition block to fetch
3315 // the exit block and create a branch from cancelation
3316 // to exit block.
3318 Builder.restoreIP(IP);
3319 auto *CaseBB = Loc.IP.getBlock();
3320 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3321 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3322 Instruction *I = Builder.CreateBr(ExitBB);
3323 IP = InsertPointTy(I->getParent(), I->getIterator());
3324 return FiniCB(IP);
3325 };
3326
3327 Directive OMPD = Directive::OMPD_sections;
3328 // Since we are using Finalization Callback here, HasFinalize
3329 // and IsCancellable have to be true
3330 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3331 /*Conditional*/ false, /*hasFinalize*/ true,
3332 /*IsCancellable*/ true);
3333}
3334
3340
3341Value *OpenMPIRBuilder::getGPUThreadID() {
3344 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3345 {});
3346}
3347
3348Value *OpenMPIRBuilder::getGPUWarpSize() {
3350 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3351}
3352
3353Value *OpenMPIRBuilder::getNVPTXWarpID() {
3354 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3355 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3356}
3357
3358Value *OpenMPIRBuilder::getNVPTXLaneID() {
3359 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3360 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3361 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3362 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3363 "nvptx_lane_id");
3364}
3365
3366Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3367 Type *ToType) {
3368 Type *FromType = From->getType();
3369 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3370 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3371 assert(FromSize > 0 && "From size must be greater than zero");
3372 assert(ToSize > 0 && "To size must be greater than zero");
3373 if (FromType == ToType)
3374 return From;
3375 if (FromSize == ToSize)
3376 return Builder.CreateBitCast(From, ToType);
3377 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3378 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3379 InsertPointTy SaveIP = Builder.saveIP();
3380 Builder.restoreIP(AllocaIP);
3381 Value *CastItem = Builder.CreateAlloca(ToType);
3382 Builder.restoreIP(SaveIP);
3383
3384 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3385 CastItem, Builder.getPtrTy(0));
3386 Builder.CreateStore(From, ValCastItem);
3387 return Builder.CreateLoad(ToType, CastItem);
3388}
3389
3390Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3391 Value *Element,
3392 Type *ElementType,
3393 Value *Offset) {
3394 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3395 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3396
3397 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3398 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3399 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3400 Value *WarpSize =
3401 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3403 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3404 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3405 Value *WarpSizeCast =
3406 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3407 Value *ShuffleCall =
3408 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3409 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3410 // down to the requested element type, otherwise storing the result would
3411 // write past the end of an element narrower than the shuffle width.
3412 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3413}
3414
3415void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3416 Value *DstAddr, Type *ElemType,
3417 Value *Offset, Type *ReductionArrayTy,
3418 bool IsByRefElem) {
3419 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3420 // Create the loop over the big sized data.
3421 // ptr = (void*)Elem;
3422 // ptrEnd = (void*) Elem + 1;
3423 // Step = 8;
3424 // while (ptr + Step < ptrEnd)
3425 // shuffle((int64_t)*ptr);
3426 // Step = 4;
3427 // while (ptr + Step < ptrEnd)
3428 // shuffle((int32_t)*ptr);
3429 // ...
3430 Type *IndexTy = Builder.getIndexTy(
3431 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3432 Value *ElemPtr = DstAddr;
3433 Value *Ptr = SrcAddr;
3434 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3435 if (Size < IntSize)
3436 continue;
3437 Type *IntType = Builder.getIntNTy(IntSize * 8);
3438 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3439 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3440 Value *SrcAddrGEP =
3441 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3442 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3443 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3444
3445 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3446 if ((Size / IntSize) > 1) {
3447 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3448 SrcAddrGEP, Builder.getPtrTy());
3449 BasicBlock *PreCondBB =
3450 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3451 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3452 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3453 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3454 emitBlock(PreCondBB, CurFunc);
3455 PHINode *PhiSrc =
3456 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3457 PhiSrc->addIncoming(Ptr, CurrentBB);
3458 PHINode *PhiDest =
3459 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3460 PhiDest->addIncoming(ElemPtr, CurrentBB);
3461 Ptr = PhiSrc;
3462 ElemPtr = PhiDest;
3463 Value *PtrDiff = Builder.CreatePtrDiff(
3464 Builder.getInt8Ty(), PtrEnd,
3465 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3466 Builder.CreateCondBr(
3467 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3468 ExitBB);
3469 emitBlock(ThenBB, CurFunc);
3470 Value *Res = createRuntimeShuffleFunction(
3471 AllocaIP,
3472 Builder.CreateAlignedLoad(
3473 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3474 IntType, Offset);
3475 Builder.CreateAlignedStore(Res, ElemPtr,
3476 M.getDataLayout().getPrefTypeAlign(ElemType));
3477 Value *LocalPtr =
3478 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3479 Value *LocalElemPtr =
3480 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3481 PhiSrc->addIncoming(LocalPtr, ThenBB);
3482 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3483 emitBranch(PreCondBB);
3484 emitBlock(ExitBB, CurFunc);
3485 } else {
3486 // The shuffled value comes back as the chunk's integer type, so the
3487 // store covers exactly this chunk regardless of what ElemType is.
3488 Value *Res = createRuntimeShuffleFunction(
3489 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3490 Builder.CreateStore(Res, ElemPtr);
3491 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3492 ElemPtr =
3493 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3494 }
3495 Size = Size % IntSize;
3496 }
3497}
3498
3499Error OpenMPIRBuilder::emitReductionListCopy(
3500 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3501 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3502 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3503 Type *IndexTy = Builder.getIndexTy(
3504 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3505 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3506
3507 // Iterates, element-by-element, through the source Reduce list and
3508 // make a copy.
3509 for (auto En : enumerate(ReductionInfos)) {
3510 const ReductionInfo &RI = En.value();
3511 Value *SrcElementAddr = nullptr;
3512 AllocaInst *DestAlloca = nullptr;
3513 Value *DestElementAddr = nullptr;
3514 Value *DestElementPtrAddr = nullptr;
3515 // Should we shuffle in an element from a remote lane?
3516 bool ShuffleInElement = false;
3517 // Set to true to update the pointer in the dest Reduce list to a
3518 // newly created element.
3519 bool UpdateDestListPtr = false;
3520
3521 // Step 1.1: Get the address for the src element in the Reduce list.
3522 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3523 ReductionArrayTy, SrcBase,
3524 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3525 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3526
3527 // Step 1.2: Create a temporary to store the element in the destination
3528 // Reduce list.
3529 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3530 ReductionArrayTy, DestBase,
3531 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3532 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3533 switch (Action) {
3535 InsertPointTy CurIP = Builder.saveIP();
3536 Builder.restoreIP(AllocaIP);
3537
3538 Type *DestAllocaType =
3539 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3540 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3541 ".omp.reduction.element");
3542 DestAlloca->setAlignment(
3543 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3544 DestElementAddr = DestAlloca;
3545 DestElementAddr =
3546 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3547 DestElementAddr->getName() + ".ascast");
3548 Builder.restoreIP(CurIP);
3549 ShuffleInElement = true;
3550 UpdateDestListPtr = true;
3551 break;
3552 }
3554 DestElementAddr =
3555 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3556 break;
3557 }
3558 }
3559
3560 // Now that all active lanes have read the element in the
3561 // Reduce list, shuffle over the value from the remote lane.
3562 if (ShuffleInElement) {
3563 Type *ShuffleType = RI.ElementType;
3564 Value *ShuffleSrcAddr = SrcElementAddr;
3565 Value *ShuffleDestAddr = DestElementAddr;
3566 AllocaInst *LocalStorage = nullptr;
3567
3568 if (IsByRefElem) {
3569 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3570 assert(RI.ByRefAllocatedType &&
3571 "Expected by-ref allocated type to be set");
3572 // For by-ref reductions, we need to copy from the remote lane the
3573 // actual value of the partial reduction computed by that remote lane;
3574 // rather than, for example, a pointer to that data or, even worse, a
3575 // pointer to the descriptor of the by-ref reduction element.
3576 ShuffleType = RI.ByRefElementType;
3577
3578 if (RI.DataPtrPtrGen) {
3579 // Descriptor-based by-ref: extract data pointer from descriptor.
3580 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3581 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3582
3583 if (!GenResult)
3584 return GenResult.takeError();
3585
3586 ShuffleSrcAddr =
3587 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3588
3589 {
3590 InsertPointTy OldIP = Builder.saveIP();
3591 Builder.restoreIP(AllocaIP);
3592
3593 LocalStorage = Builder.CreateAlloca(ShuffleType);
3594 Builder.restoreIP(OldIP);
3595 ShuffleDestAddr = LocalStorage;
3596 }
3597 } else {
3598 // Non-descriptor by-ref: the pointer already references data
3599 // directly. Shuffle into the destination alloca.
3600 ShuffleDestAddr = DestElementAddr;
3601 }
3602 }
3603
3604 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3605 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3606
3607 if (IsByRefElem && RI.DataPtrPtrGen) {
3608 // Copy descriptor from source and update base_ptr to shuffled data
3609 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3610 DestAlloca, Builder.getPtrTy(), ".ascast");
3611
3612 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3613 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3614 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3615
3616 if (!GenResult)
3617 return GenResult.takeError();
3618 }
3619 } else {
3620 switch (RI.EvaluationKind) {
3621 case EvalKind::Scalar: {
3622 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3623 // Store the source element value to the dest element address.
3624 Builder.CreateStore(Elem, DestElementAddr);
3625 break;
3626 }
3627 case EvalKind::Complex: {
3628 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3629 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3630 Value *SrcReal = Builder.CreateLoad(
3631 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3632 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3633 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3634 Value *SrcImg = Builder.CreateLoad(
3635 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3636
3637 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3638 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3639 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3640 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3641 Builder.CreateStore(SrcReal, DestRealPtr);
3642 Builder.CreateStore(SrcImg, DestImgPtr);
3643 break;
3644 }
3645 case EvalKind::Aggregate: {
3646 Value *SizeVal = Builder.getInt64(
3647 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3648 Builder.CreateMemCpy(
3649 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3650 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3651 SizeVal, false);
3652 break;
3653 }
3654 };
3655 }
3656
3657 // Step 3.1: Modify reference in dest Reduce list as needed.
3658 // Modifying the reference in Reduce list to point to the newly
3659 // created element. The element is live in the current function
3660 // scope and that of functions it invokes (i.e., reduce_function).
3661 // RemoteReduceData[i] = (void*)&RemoteElem
3662 if (UpdateDestListPtr) {
3663 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3664 DestElementAddr, Builder.getPtrTy(),
3665 DestElementAddr->getName() + ".ascast");
3666 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3667 }
3668 }
3669
3670 return Error::success();
3671}
3672
3673Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3674 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3675 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3676 IRBuilder<>::InsertPointGuard IPG(Builder);
3677 LLVMContext &Ctx = M.getContext();
3678 FunctionType *FuncTy = FunctionType::get(
3679 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3680 /* IsVarArg */ false);
3681 Function *WcFunc =
3683 "_omp_reduction_inter_warp_copy_func", &M);
3684 WcFunc->setCallingConv(Config.getRuntimeCC());
3685 WcFunc->setAttributes(FuncAttrs);
3686 WcFunc->addParamAttr(0, Attribute::NoUndef);
3687 WcFunc->addParamAttr(1, Attribute::NoUndef);
3688 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3689 Builder.SetInsertPoint(EntryBB);
3690 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3691
3692 // ReduceList: thread local Reduce list.
3693 // At the stage of the computation when this function is called, partially
3694 // aggregated values reside in the first lane of every active warp.
3695 Argument *ReduceListArg = WcFunc->getArg(0);
3696 // NumWarps: number of warps active in the parallel region. This could
3697 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3698 Argument *NumWarpsArg = WcFunc->getArg(1);
3699
3700 // This array is used as a medium to transfer, one reduce element at a time,
3701 // the data from the first lane of every warp to lanes in the first warp
3702 // in order to perform the final step of a reduction in a parallel region
3703 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3704 // for reduced latency, as well as to have a distinct copy for concurrently
3705 // executing target regions. The array is declared with common linkage so
3706 // as to be shared across compilation units.
3707 StringRef TransferMediumName =
3708 "__openmp_nvptx_data_transfer_temporary_storage";
3709 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3710 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3711 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3712 if (!TransferMedium) {
3713 TransferMedium = new GlobalVariable(
3714 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3715 UndefValue::get(ArrayTy), TransferMediumName,
3716 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3717 /*AddressSpace=*/3);
3718 }
3719
3720 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3721 Value *GPUThreadID = getGPUThreadID();
3722 // nvptx_lane_id = nvptx_id % warpsize
3723 Value *LaneID = getNVPTXLaneID();
3724 // nvptx_warp_id = nvptx_id / warpsize
3725 Value *WarpID = getNVPTXWarpID();
3726
3727 InsertPointTy AllocaIP =
3728 InsertPointTy(Builder.GetInsertBlock(),
3729 Builder.GetInsertBlock()->getFirstInsertionPt());
3730 Type *Arg0Type = ReduceListArg->getType();
3731 Type *Arg1Type = NumWarpsArg->getType();
3732 Builder.restoreIP(AllocaIP);
3733 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3734 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3735 AllocaInst *NumWarpsAlloca =
3736 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3737 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3738 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3739 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3740 NumWarpsAlloca, Builder.getPtrTy(0),
3741 NumWarpsAlloca->getName() + ".ascast");
3742 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3743 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3744 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3745 InsertPointTy CodeGenIP =
3746 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3747 Builder.restoreIP(CodeGenIP);
3748
3749 Value *ReduceList =
3750 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3751
3752 for (auto En : enumerate(ReductionInfos)) {
3753 //
3754 // Warp master copies reduce element to transfer medium in __shared__
3755 // memory.
3756 //
3757 const ReductionInfo &RI = En.value();
3758 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3759 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3760 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3761 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3762 Type *CType = Builder.getIntNTy(TySize * 8);
3763
3764 unsigned NumIters = RealTySize / TySize;
3765 if (NumIters == 0)
3766 continue;
3767 Value *Cnt = nullptr;
3768 Value *CntAddr = nullptr;
3769 BasicBlock *PrecondBB = nullptr;
3770 BasicBlock *ExitBB = nullptr;
3771 if (NumIters > 1) {
3772 CodeGenIP = Builder.saveIP();
3773 Builder.restoreIP(AllocaIP);
3774 CntAddr =
3775 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3776
3777 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3778 CntAddr->getName() + ".ascast");
3779 Builder.restoreIP(CodeGenIP);
3780 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3781 CntAddr,
3782 /*Volatile=*/false);
3783 PrecondBB = BasicBlock::Create(Ctx, "precond");
3784 ExitBB = BasicBlock::Create(Ctx, "exit");
3785 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3786 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3787 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3788 /*Volatile=*/false);
3789 Value *Cmp = Builder.CreateICmpULT(
3790 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3791 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3792 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3793 }
3794
3795 // kmpc_barrier.
3796 InsertPointOrErrorTy BarrierIP1 =
3798 omp::Directive::OMPD_unknown,
3799 /* ForceSimpleCall */ false,
3800 /* CheckCancelFlag */ true);
3801 if (!BarrierIP1)
3802 return BarrierIP1.takeError();
3803 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3804 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3805 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3806
3807 // if (lane_id == 0)
3808 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3809 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3810 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3811
3812 // Reduce element = LocalReduceList[i]
3813 auto *RedListArrayTy =
3814 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3815 Type *IndexTy = Builder.getIndexTy(
3816 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3817 Value *ElemPtrPtr =
3818 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3819 {ConstantInt::get(IndexTy, 0),
3820 ConstantInt::get(IndexTy, En.index())});
3821 // elemptr = ((CopyType*)(elemptrptr)) + I
3822 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3823
3824 if (IsByRefElem && RI.DataPtrPtrGen) {
3825 InsertPointOrErrorTy GenRes =
3826 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3827
3828 if (!GenRes)
3829 return GenRes.takeError();
3830
3831 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3832 }
3833
3834 if (NumIters > 1)
3835 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3836
3837 // Get pointer to location in transfer medium.
3838 // MediumPtr = &medium[warp_id]
3839 Value *MediumPtr = Builder.CreateInBoundsGEP(
3840 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3841 // elem = *elemptr
3842 //*MediumPtr = elem
3843 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3844 // Store the source element value to the dest element address.
3845 Builder.CreateStore(Elem, MediumPtr,
3846 /*IsVolatile*/ true);
3847 Builder.CreateBr(MergeBB);
3848
3849 // else
3850 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3851 Builder.CreateBr(MergeBB);
3852
3853 // endif
3854 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3855 InsertPointOrErrorTy BarrierIP2 =
3857 omp::Directive::OMPD_unknown,
3858 /* ForceSimpleCall */ false,
3859 /* CheckCancelFlag */ true);
3860 if (!BarrierIP2)
3861 return BarrierIP2.takeError();
3862
3863 // Warp 0 copies reduce element from transfer medium
3864 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3865 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3866 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3867
3868 Value *NumWarpsVal =
3869 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3870 // Up to 32 threads in warp 0 are active.
3871 Value *IsActiveThread =
3872 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3873 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3874
3875 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3876
3877 // SecMediumPtr = &medium[tid]
3878 // SrcMediumVal = *SrcMediumPtr
3879 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3880 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3881 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3882 Value *TargetElemPtrPtr =
3883 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3884 {ConstantInt::get(IndexTy, 0),
3885 ConstantInt::get(IndexTy, En.index())});
3886 Value *TargetElemPtrVal =
3887 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3888 Value *TargetElemPtr = TargetElemPtrVal;
3889
3890 if (IsByRefElem && RI.DataPtrPtrGen) {
3891 InsertPointOrErrorTy GenRes =
3892 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3893
3894 if (!GenRes)
3895 return GenRes.takeError();
3896
3897 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3898 }
3899
3900 if (NumIters > 1)
3901 TargetElemPtr =
3902 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3903
3904 // *TargetElemPtr = SrcMediumVal;
3905 Value *SrcMediumValue =
3906 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3907 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3908 Builder.CreateBr(W0MergeBB);
3909
3910 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3911 Builder.CreateBr(W0MergeBB);
3912
3913 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3914
3915 if (NumIters > 1) {
3916 Cnt = Builder.CreateNSWAdd(
3917 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3918 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3919
3920 auto *CurFn = Builder.GetInsertBlock()->getParent();
3921 emitBranch(PrecondBB);
3922 emitBlock(ExitBB, CurFn);
3923 }
3924 RealTySize %= TySize;
3925 }
3926 }
3927
3928 Builder.CreateRetVoid();
3929
3930 return WcFunc;
3931}
3932
3933Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3934 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3935 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3936 LLVMContext &Ctx = M.getContext();
3937 IRBuilder<>::InsertPointGuard IPG(Builder);
3938 FunctionType *FuncTy =
3939 FunctionType::get(Builder.getVoidTy(),
3940 {Builder.getPtrTy(), Builder.getInt16Ty(),
3941 Builder.getInt16Ty(), Builder.getInt16Ty()},
3942 /* IsVarArg */ false);
3943 Function *SarFunc =
3945 "_omp_reduction_shuffle_and_reduce_func", &M);
3946 SarFunc->setCallingConv(Config.getRuntimeCC());
3947 SarFunc->setAttributes(FuncAttrs);
3948 SarFunc->addParamAttr(0, Attribute::NoUndef);
3949 SarFunc->addParamAttr(1, Attribute::NoUndef);
3950 SarFunc->addParamAttr(2, Attribute::NoUndef);
3951 SarFunc->addParamAttr(3, Attribute::NoUndef);
3952 SarFunc->addParamAttr(1, Attribute::SExt);
3953 SarFunc->addParamAttr(2, Attribute::SExt);
3954 SarFunc->addParamAttr(3, Attribute::SExt);
3955 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3956 Builder.SetInsertPoint(EntryBB);
3957 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3958
3959 // Thread local Reduce list used to host the values of data to be reduced.
3960 Argument *ReduceListArg = SarFunc->getArg(0);
3961 // Current lane id; could be logical.
3962 Argument *LaneIDArg = SarFunc->getArg(1);
3963 // Offset of the remote source lane relative to the current lane.
3964 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
3965 // Algorithm version. This is expected to be known at compile time.
3966 Argument *AlgoVerArg = SarFunc->getArg(3);
3967
3968 Type *ReduceListArgType = ReduceListArg->getType();
3969 Type *LaneIDArgType = LaneIDArg->getType();
3970 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
3971 Value *ReduceListAlloca = Builder.CreateAlloca(
3972 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
3973 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3974 LaneIDArg->getName() + ".addr");
3975 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
3976 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
3977 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3978 AlgoVerArg->getName() + ".addr");
3979 ArrayType *RedListArrayTy =
3980 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3981
3982 // Create a local thread-private variable to host the Reduce list
3983 // from a remote lane.
3984 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
3985 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
3986
3987 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3988 ReduceListAlloca, ReduceListArgType,
3989 ReduceListAlloca->getName() + ".ascast");
3990 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3991 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
3992 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3993 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
3994 RemoteLaneOffsetAlloca->getName() + ".ascast");
3995 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3996 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
3997 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3998 RemoteReductionListAlloca, Builder.getPtrTy(),
3999 RemoteReductionListAlloca->getName() + ".ascast");
4000
4001 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
4002 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
4003 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
4004 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
4005
4006 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
4007 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4008 Value *RemoteLaneOffset =
4009 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4010 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4011
4012 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4013
4014 // This loop iterates through the list of reduce elements and copies,
4015 // element by element, from a remote lane in the warp to RemoteReduceList,
4016 // hosted on the thread's stack.
4017 Error EmitRedLsCpRes = emitReductionListCopy(
4018 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4019 ReduceList, RemoteListAddrCast, IsByRef,
4020 {RemoteLaneOffset, nullptr, nullptr});
4021
4022 if (EmitRedLsCpRes)
4023 return EmitRedLsCpRes;
4024
4025 // The actions to be performed on the Remote Reduce list is dependent
4026 // on the algorithm version.
4027 //
4028 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4029 // LaneId % 2 == 0 && Offset > 0):
4030 // do the reduction value aggregation
4031 //
4032 // The thread local variable Reduce list is mutated in place to host the
4033 // reduced data, which is the aggregated value produced from local and
4034 // remote lanes.
4035 //
4036 // Note that AlgoVer is expected to be a constant integer known at compile
4037 // time.
4038 // When AlgoVer==0, the first conjunction evaluates to true, making
4039 // the entire predicate true during compile time.
4040 // When AlgoVer==1, the second conjunction has only the second part to be
4041 // evaluated during runtime. Other conjunctions evaluates to false
4042 // during compile time.
4043 // When AlgoVer==2, the third conjunction has only the second part to be
4044 // evaluated during runtime. Other conjunctions evaluates to false
4045 // during compile time.
4046 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4047 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4048 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4049 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4050 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4051 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4052 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4053 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4054 Value *RemoteOffsetComp =
4055 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4056 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4057 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4058 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4059
4060 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4061 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4062 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4063
4064 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4065 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4066 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4067 ReduceList, Builder.getPtrTy());
4068 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4069 RemoteListAddrCast, Builder.getPtrTy());
4070 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4071 ->addFnAttr(Attribute::NoUnwind);
4072 Builder.CreateBr(MergeBB);
4073
4074 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4075 Builder.CreateBr(MergeBB);
4076
4077 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4078
4079 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4080 // Reduce list.
4081 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4082 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4083 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4084
4085 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4086 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4087 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4088 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4089
4090 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4091
4092 EmitRedLsCpRes = emitReductionListCopy(
4093 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4094 RemoteListAddrCast, ReduceList, IsByRef);
4095
4096 if (EmitRedLsCpRes)
4097 return EmitRedLsCpRes;
4098
4099 Builder.CreateBr(CpyMergeBB);
4100
4101 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4102 Builder.CreateBr(CpyMergeBB);
4103
4104 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4105
4106 Builder.CreateRetVoid();
4107
4108 return SarFunc;
4109}
4110
4112OpenMPIRBuilder::generateReductionDescriptor(
4113 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4114 Type *DescriptorType,
4115 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4116 DataPtrPtrGen) {
4117
4118 // Copy the source descriptor to preserve all metadata (rank, extents,
4119 // strides, etc.)
4120 Value *DescriptorSize =
4121 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4122 Builder.CreateMemCpy(
4123 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4124 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4125 DescriptorSize);
4126
4127 // Update the base pointer field to point to the local shuffled data
4128 Value *DataPtrField;
4129 InsertPointOrErrorTy GenResult =
4130 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4131
4132 if (!GenResult)
4133 return GenResult.takeError();
4134
4135 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4136 DataPtr, Builder.getPtrTy(), ".ascast"),
4137 DataPtrField);
4138
4139 return Builder.saveIP();
4140}
4141
4142Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4143 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4144 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4145 InsertPointTy OldIP = Builder.saveIP();
4146 Builder.restoreIP(AllocaIP);
4147
4148 AllocaInst *DescriptorAlloca =
4149 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4150 DescriptorAlloca->setAlignment(
4151 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4152 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4153 DescriptorAlloca, DescriptorPtrTy,
4154 DescriptorAlloca->getName() + ".ascast");
4155
4156 Builder.restoreIP(OldIP);
4157
4158 InsertPointOrErrorTy GenResult =
4159 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4160 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4161 if (!GenResult)
4162 return GenResult.takeError();
4163
4164 return DescriptorAddr;
4165}
4166
4167Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4168 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4169 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4170 IRBuilder<>::InsertPointGuard IPG(Builder);
4171 LLVMContext &Ctx = M.getContext();
4172 FunctionType *FuncTy = FunctionType::get(
4173 Builder.getVoidTy(),
4174 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4175 /* IsVarArg */ false);
4176 Function *LtGCFunc =
4178 "_omp_reduction_list_to_global_copy_func", &M);
4179 LtGCFunc->setAttributes(FuncAttrs);
4180 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4181 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4182 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4183
4184 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4185 Builder.SetInsertPoint(EntryBlock);
4186 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4187
4188 // Buffer: global reduction buffer.
4189 Argument *BufferArg = LtGCFunc->getArg(0);
4190 // Idx: index of the buffer.
4191 Argument *IdxArg = LtGCFunc->getArg(1);
4192 // ReduceList: thread local Reduce list.
4193 Argument *ReduceListArg = LtGCFunc->getArg(2);
4194
4195 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4196 BufferArg->getName() + ".addr");
4197 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4198 IdxArg->getName() + ".addr");
4199 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4200 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4201 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4202 BufferArgAlloca, Builder.getPtrTy(),
4203 BufferArgAlloca->getName() + ".ascast");
4204 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4205 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4206 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4207 ReduceListArgAlloca, Builder.getPtrTy(),
4208 ReduceListArgAlloca->getName() + ".ascast");
4209
4210 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4211 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4212 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4213
4214 Value *LocalReduceList =
4215 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4216 Value *BufferArgVal =
4217 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4218 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4219 Type *IndexTy = Builder.getIndexTy(
4220 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4221 for (auto En : enumerate(ReductionInfos)) {
4222 const ReductionInfo &RI = En.value();
4223 auto *RedListArrayTy =
4224 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4225 // Reduce element = LocalReduceList[i]
4226 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4227 RedListArrayTy, LocalReduceList,
4228 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4229 // elemptr = ((CopyType*)(elemptrptr)) + I
4230 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4231
4232 // Global = Buffer.VD[Idx];
4233 Value *BufferVD =
4234 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4235 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4236 ReductionsBufferTy, BufferVD, 0, En.index());
4237
4238 switch (RI.EvaluationKind) {
4239 case EvalKind::Scalar: {
4240 Value *TargetElement;
4241
4242 if (IsByRef.empty() || !IsByRef[En.index()]) {
4243 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4244 } else {
4245 if (RI.DataPtrPtrGen) {
4246 InsertPointOrErrorTy GenResult =
4247 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4248
4249 if (!GenResult)
4250 return GenResult.takeError();
4251
4252 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4253 }
4254 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4255 }
4256
4257 Builder.CreateStore(TargetElement, GlobVal);
4258 break;
4259 }
4260 case EvalKind::Complex: {
4261 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4262 RI.ElementType, ElemPtr, 0, 0, ".realp");
4263 Value *SrcReal = Builder.CreateLoad(
4264 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4265 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4266 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4267 Value *SrcImg = Builder.CreateLoad(
4268 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4269
4270 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4271 RI.ElementType, GlobVal, 0, 0, ".realp");
4272 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4273 RI.ElementType, GlobVal, 0, 1, ".imagp");
4274 Builder.CreateStore(SrcReal, DestRealPtr);
4275 Builder.CreateStore(SrcImg, DestImgPtr);
4276 break;
4277 }
4278 case EvalKind::Aggregate: {
4279 Value *SizeVal =
4280 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4281 Builder.CreateMemCpy(
4282 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4283 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4284 break;
4285 }
4286 }
4287 }
4288
4289 Builder.CreateRetVoid();
4290 return LtGCFunc;
4291}
4292
4293Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4294 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4295 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4296 IRBuilder<>::InsertPointGuard IPG(Builder);
4297 LLVMContext &Ctx = M.getContext();
4298 FunctionType *FuncTy = FunctionType::get(
4299 Builder.getVoidTy(),
4300 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4301 /* IsVarArg */ false);
4302 Function *LtGRFunc =
4304 "_omp_reduction_list_to_global_reduce_func", &M);
4305 LtGRFunc->setAttributes(FuncAttrs);
4306 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4307 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4308 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4309
4310 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4311 Builder.SetInsertPoint(EntryBlock);
4312 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4313
4314 // Buffer: global reduction buffer.
4315 Argument *BufferArg = LtGRFunc->getArg(0);
4316 // Idx: index of the buffer.
4317 Argument *IdxArg = LtGRFunc->getArg(1);
4318 // ReduceList: thread local Reduce list.
4319 Argument *ReduceListArg = LtGRFunc->getArg(2);
4320
4321 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4322 BufferArg->getName() + ".addr");
4323 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4324 IdxArg->getName() + ".addr");
4325 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4326 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4327 auto *RedListArrayTy =
4328 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4329
4330 // 1. Build a list of reduction variables.
4331 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4332 Value *LocalReduceList =
4333 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4334
4335 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4336
4337 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4338 BufferArgAlloca, Builder.getPtrTy(),
4339 BufferArgAlloca->getName() + ".ascast");
4340 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4341 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4342 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4343 ReduceListArgAlloca, Builder.getPtrTy(),
4344 ReduceListArgAlloca->getName() + ".ascast");
4345 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4346 LocalReduceList, Builder.getPtrTy(),
4347 LocalReduceList->getName() + ".ascast");
4348
4349 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4350 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4351 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4352
4353 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4354 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4355 Type *IndexTy = Builder.getIndexTy(
4356 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4357 for (auto En : enumerate(ReductionInfos)) {
4358 const ReductionInfo &RI = En.value();
4359
4360 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4361 RedListArrayTy, LocalReduceListAddrCast,
4362 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4363 Value *BufferVD =
4364 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4365 // Global = Buffer.VD[Idx];
4366 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4367 ReductionsBufferTy, BufferVD, 0, En.index());
4368
4369 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4370 // Get source descriptor from the reduce list argument
4371 Value *ReduceList =
4372 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4373 Value *SrcElementPtrPtr =
4374 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4375 {ConstantInt::get(IndexTy, 0),
4376 ConstantInt::get(IndexTy, En.index())});
4377 Value *SrcDescriptorAddr =
4378 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4379
4380 // Copy descriptor from source and update base_ptr to global buffer data
4381 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4382 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4383 if (!ByRefAlloc)
4384 return ByRefAlloc.takeError();
4385
4386 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4387 } else {
4388 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4389 }
4390 }
4391
4392 // Call reduce_function(GlobalReduceList, ReduceList)
4393 Value *ReduceList =
4394 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4395 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4396 ->addFnAttr(Attribute::NoUnwind);
4397 Builder.CreateRetVoid();
4398 return LtGRFunc;
4399}
4400
4401Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4402 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4403 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4404 IRBuilder<>::InsertPointGuard IPG(Builder);
4405 LLVMContext &Ctx = M.getContext();
4406 FunctionType *FuncTy = FunctionType::get(
4407 Builder.getVoidTy(),
4408 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4409 /* IsVarArg */ false);
4410 Function *GtLCFunc =
4412 "_omp_reduction_global_to_list_copy_func", &M);
4413 GtLCFunc->setAttributes(FuncAttrs);
4414 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4415 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4416 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4417
4418 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4419 Builder.SetInsertPoint(EntryBlock);
4420 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4421
4422 // Buffer: global reduction buffer.
4423 Argument *BufferArg = GtLCFunc->getArg(0);
4424 // Idx: index of the buffer.
4425 Argument *IdxArg = GtLCFunc->getArg(1);
4426 // ReduceList: thread local Reduce list.
4427 Argument *ReduceListArg = GtLCFunc->getArg(2);
4428
4429 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4430 BufferArg->getName() + ".addr");
4431 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4432 IdxArg->getName() + ".addr");
4433 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4434 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4435 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4436 BufferArgAlloca, Builder.getPtrTy(),
4437 BufferArgAlloca->getName() + ".ascast");
4438 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4439 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4440 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4441 ReduceListArgAlloca, Builder.getPtrTy(),
4442 ReduceListArgAlloca->getName() + ".ascast");
4443 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4444 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4445 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4446
4447 Value *LocalReduceList =
4448 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4449 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4450 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4451 Type *IndexTy = Builder.getIndexTy(
4452 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4453 for (auto En : enumerate(ReductionInfos)) {
4454 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4455 auto *RedListArrayTy =
4456 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4457 // Reduce element = LocalReduceList[i]
4458 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4459 RedListArrayTy, LocalReduceList,
4460 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4461 // elemptr = ((CopyType*)(elemptrptr)) + I
4462 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4463 // Global = Buffer.VD[Idx];
4464 Value *BufferVD =
4465 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4466 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4467 ReductionsBufferTy, BufferVD, 0, En.index());
4468
4469 switch (RI.EvaluationKind) {
4470 case EvalKind::Scalar: {
4471 Type *ElemType = RI.ElementType;
4472
4473 if (!IsByRef.empty() && IsByRef[En.index()]) {
4474 ElemType = RI.ByRefElementType;
4475 if (RI.DataPtrPtrGen) {
4476 InsertPointOrErrorTy GenResult =
4477 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4478
4479 if (!GenResult)
4480 return GenResult.takeError();
4481
4482 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4483 }
4484 }
4485
4486 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4487 Builder.CreateStore(TargetElement, ElemPtr);
4488 break;
4489 }
4490 case EvalKind::Complex: {
4491 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4492 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4493 Value *SrcReal = Builder.CreateLoad(
4494 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4495 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4496 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4497 Value *SrcImg = Builder.CreateLoad(
4498 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4499
4500 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4501 RI.ElementType, ElemPtr, 0, 0, ".realp");
4502 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4503 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4504 Builder.CreateStore(SrcReal, DestRealPtr);
4505 Builder.CreateStore(SrcImg, DestImgPtr);
4506 break;
4507 }
4508 case EvalKind::Aggregate: {
4509 Value *SizeVal =
4510 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4511 Builder.CreateMemCpy(
4512 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4513 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4514 SizeVal, false);
4515 break;
4516 }
4517 }
4518 }
4519
4520 Builder.CreateRetVoid();
4521 return GtLCFunc;
4522}
4523
4524Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4525 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4526 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4527 IRBuilder<>::InsertPointGuard IPG(Builder);
4528 LLVMContext &Ctx = M.getContext();
4529 auto *FuncTy = FunctionType::get(
4530 Builder.getVoidTy(),
4531 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4532 /* IsVarArg */ false);
4533 Function *GtLRFunc =
4535 "_omp_reduction_global_to_list_reduce_func", &M);
4536 GtLRFunc->setAttributes(FuncAttrs);
4537 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4538 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4539 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4540
4541 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4542 Builder.SetInsertPoint(EntryBlock);
4543 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4544
4545 // Buffer: global reduction buffer.
4546 Argument *BufferArg = GtLRFunc->getArg(0);
4547 // Idx: index of the buffer.
4548 Argument *IdxArg = GtLRFunc->getArg(1);
4549 // ReduceList: thread local Reduce list.
4550 Argument *ReduceListArg = GtLRFunc->getArg(2);
4551
4552 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4553 BufferArg->getName() + ".addr");
4554 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4555 IdxArg->getName() + ".addr");
4556 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4557 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4558 ArrayType *RedListArrayTy =
4559 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4560
4561 // 1. Build a list of reduction variables.
4562 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4563 Value *LocalReduceList =
4564 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4565
4566 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4567
4568 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4569 BufferArgAlloca, Builder.getPtrTy(),
4570 BufferArgAlloca->getName() + ".ascast");
4571 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4572 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4573 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4574 ReduceListArgAlloca, Builder.getPtrTy(),
4575 ReduceListArgAlloca->getName() + ".ascast");
4576 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4577 LocalReduceList, Builder.getPtrTy(),
4578 LocalReduceList->getName() + ".ascast");
4579
4580 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4581 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4582 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4583
4584 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4585 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4586 Type *IndexTy = Builder.getIndexTy(
4587 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4588 for (auto En : enumerate(ReductionInfos)) {
4589 const ReductionInfo &RI = En.value();
4590
4591 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4592 RedListArrayTy, ReductionList,
4593 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4594 // Global = Buffer.VD[Idx];
4595 Value *BufferVD =
4596 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4597 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4598 ReductionsBufferTy, BufferVD, 0, En.index());
4599
4600 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4601 // Get source descriptor from the reduce list
4602 Value *ReduceListVal =
4603 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4604 Value *SrcElementPtrPtr =
4605 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4606 {ConstantInt::get(IndexTy, 0),
4607 ConstantInt::get(IndexTy, En.index())});
4608 Value *SrcDescriptorAddr =
4609 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4610
4611 // Copy descriptor from source and update base_ptr to global buffer data
4612 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4613 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4614 if (!ByRefAlloc)
4615 return ByRefAlloc.takeError();
4616
4617 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4618 } else {
4619 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4620 }
4621 }
4622
4623 // Call reduce_function(ReduceList, GlobalReduceList)
4624 Value *ReduceList =
4625 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4626 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4627 ->addFnAttr(Attribute::NoUnwind);
4628 Builder.CreateRetVoid();
4629 return GtLRFunc;
4630}
4631
4632std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4633 std::string Suffix =
4634 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4635 return (Name + Suffix).str();
4636}
4637
4638Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4639 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4641 AttributeList FuncAttrs) {
4642 IRBuilder<>::InsertPointGuard IPG(Builder);
4643 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4644 {Builder.getPtrTy(), Builder.getPtrTy()},
4645 /* IsVarArg */ false);
4646 std::string Name = getReductionFuncName(ReducerName);
4647 Function *ReductionFunc =
4649 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4650 ReductionFunc->setAttributes(FuncAttrs);
4651 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4652 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4653 BasicBlock *EntryBB =
4654 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4655 Builder.SetInsertPoint(EntryBB);
4656 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4657
4658 // Need to alloca memory here and deal with the pointers before getting
4659 // LHS/RHS pointers out
4660 Value *LHSArrayPtr = nullptr;
4661 Value *RHSArrayPtr = nullptr;
4662 Argument *Arg0 = ReductionFunc->getArg(0);
4663 Argument *Arg1 = ReductionFunc->getArg(1);
4664 Type *Arg0Type = Arg0->getType();
4665 Type *Arg1Type = Arg1->getType();
4666
4667 Value *LHSAlloca =
4668 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4669 Value *RHSAlloca =
4670 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4671 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4672 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4673 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4674 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4675 Builder.CreateStore(Arg0, LHSAddrCast);
4676 Builder.CreateStore(Arg1, RHSAddrCast);
4677 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4678 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4679
4680 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4681 Type *IndexTy = Builder.getIndexTy(
4682 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4683 SmallVector<Value *> LHSPtrs, RHSPtrs;
4684 for (auto En : enumerate(ReductionInfos)) {
4685 const ReductionInfo &RI = En.value();
4686 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4687 RedArrayTy, RHSArrayPtr,
4688 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4689 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4690 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4691 RHSI8Ptr, RI.PrivateVariable->getType(),
4692 RHSI8Ptr->getName() + ".ascast");
4693
4694 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4695 RedArrayTy, LHSArrayPtr,
4696 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4697 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4698 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4699 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4700
4702 LHSPtrs.emplace_back(LHSPtr);
4703 RHSPtrs.emplace_back(RHSPtr);
4704 } else {
4705 Value *LHS = LHSPtr;
4706 Value *RHS = RHSPtr;
4707
4708 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4709 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4710 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4711 }
4712
4713 Value *Reduced;
4714 InsertPointOrErrorTy AfterIP =
4715 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4716 if (!AfterIP)
4717 return AfterIP.takeError();
4718 if (!Builder.GetInsertBlock())
4719 return ReductionFunc;
4720
4721 Builder.restoreIP(*AfterIP);
4722
4723 if (!IsByRef.empty() && !IsByRef[En.index()])
4724 Builder.CreateStore(Reduced, LHSPtr);
4725 }
4726 }
4727
4729 for (auto En : enumerate(ReductionInfos)) {
4730 unsigned Index = En.index();
4731 const ReductionInfo &RI = En.value();
4732 Value *LHSFixupPtr, *RHSFixupPtr;
4733 Builder.restoreIP(RI.ReductionGenClang(
4734 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4735
4736 // Fix the CallBack code genereated to use the correct Values for the LHS
4737 // and RHS
4738 LHSFixupPtr->replaceUsesWithIf(
4739 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4740 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4741 ReductionFunc;
4742 });
4743 RHSFixupPtr->replaceUsesWithIf(
4744 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4745 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4746 ReductionFunc;
4747 });
4748 }
4749
4750 Builder.CreateRetVoid();
4751 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4752 // to the entry block (this is dones for higher opt levels by later passes in
4753 // the pipeline). This has caused issues because non-entry `alloca`s force the
4754 // function to use dynamic stack allocations and we might run out of scratch
4755 // memory.
4756 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4757
4758 return ReductionFunc;
4759}
4760
4761static void
4763 bool IsGPU) {
4764 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4765 (void)RI;
4766 assert(RI.Variable && "expected non-null variable");
4767 assert(RI.PrivateVariable && "expected non-null private variable");
4768 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4769 "expected non-null reduction generator callback");
4770 if (!IsGPU) {
4771 assert(
4772 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4773 "expected variables and their private equivalents to have the same "
4774 "type");
4775 }
4776 assert(RI.Variable->getType()->isPointerTy() &&
4777 "expected variables to be pointers");
4778 }
4779}
4780
4781// The atomic cross-team reduction fast path applies when every reduction in the
4782// set can be represented by an atomicrmw. Clang only populates it for scalar
4783// reductions with a supported atomic operator.
4786 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4787 return static_cast<bool>(RI.AtomicReductionGen);
4788 });
4789}
4790
4792 const LocationDescription &Loc, InsertPointTy AllocaIP,
4793 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4794 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4795 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4796 Value *SrcLocInfo) {
4797 if (!updateToLocation(Loc))
4798 return InsertPointTy();
4799 Builder.restoreIP(CodeGenIP);
4800 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4801 LLVMContext &Ctx = M.getContext();
4802
4803 // Source location for the ident struct
4804 if (!SrcLocInfo) {
4805 uint32_t SrcLocStrSize;
4806 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4807 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4808 }
4809
4810 if (ReductionInfos.size() == 0)
4811 return Builder.saveIP();
4812
4813 BasicBlock *ContinuationBlock = nullptr;
4815 // Copied code from createReductions
4816 BasicBlock *InsertBlock = Loc.IP.getBlock();
4817 ContinuationBlock =
4818 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
4819 InsertBlock->getTerminator()->eraseFromParent();
4820 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
4821 }
4822
4823 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4824 AttributeList FuncAttrs;
4825 AttrBuilder AttrBldr(Ctx);
4826 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4827 AttrBldr.addAttribute(Attr);
4828 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4829 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4830
4831 CodeGenIP = Builder.saveIP();
4832 Expected<Function *> ReductionResult = createReductionFunction(
4833 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4834 ReductionGenCBKind, FuncAttrs);
4835 if (!ReductionResult)
4836 return ReductionResult.takeError();
4837 Function *ReductionFunc = *ReductionResult;
4838 Builder.restoreIP(CodeGenIP);
4839
4840 // Set the grid value in the config needed for lowering later on
4841 if (GridValue.has_value())
4842 Config.setGridValue(GridValue.value());
4843 else
4844 Config.setGridValue(getGridValue(T, ReductionFunc));
4845
4846 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4847 // RedList, shuffle_reduce_func, interwarp_copy_func);
4848 // or
4849 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4850 Value *Res;
4851
4852 // 1. Build a list of reduction variables.
4853 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4854 auto Size = ReductionInfos.size();
4855 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4856 Type *FuncPtrTy =
4857 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4858 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4859 CodeGenIP = Builder.saveIP();
4860 Builder.restoreIP(AllocaIP);
4861 Value *ReductionListAlloca =
4862 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4863 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4864 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4865 Builder.restoreIP(CodeGenIP);
4866 Type *IndexTy = Builder.getIndexTy(
4867 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4868 for (auto En : enumerate(ReductionInfos)) {
4869 const ReductionInfo &RI = En.value();
4870 Value *ElemPtr = Builder.CreateInBoundsGEP(
4871 RedArrayTy, ReductionList,
4872 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4873
4874 Value *PrivateVar = RI.PrivateVariable;
4875 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4876 if (IsByRefElem)
4877 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4878
4879 Value *CastElem =
4880 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4881 Builder.CreateStore(CastElem, ElemPtr);
4882 }
4883 CodeGenIP = Builder.saveIP();
4884 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4885 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4886
4887 if (!SarFunc)
4888 return SarFunc.takeError();
4889
4890 Expected<Function *> CopyResult =
4891 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4892 if (!CopyResult)
4893 return CopyResult.takeError();
4894 Function *WcFunc = *CopyResult;
4895 Builder.restoreIP(CodeGenIP);
4896
4897 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4898
4899 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4900 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4901 // not currently use it. It is computed here conservatively as max(element
4902 // sizes) * N rather than the exact sum, which over-calculates the size for
4903 // mixed reduction types but is harmless given the argument is unused.
4904 // TODO: Consider dropping this computation if the runtime API is ever revised
4905 // to remove the unused parameter.
4906 unsigned MaxDataSize = 0;
4907 SmallVector<Type *> ReductionTypeArgs;
4908 for (auto En : enumerate(ReductionInfos)) {
4909 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4910 // the actual data size stored in the global reduction buffer, consistent
4911 // with the ReductionsBufferTy struct used for GEP offsets below.
4912 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4913 ? En.value().ByRefElementType
4914 : En.value().ElementType;
4915 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4916 if (Size > MaxDataSize)
4917 MaxDataSize = Size;
4918 ReductionTypeArgs.emplace_back(RedTypeArg);
4919 }
4920 Value *ReductionDataSize =
4921 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4922
4923 // Helper function to copy thread-local data back to the original reduction
4924 // list.
4925 Function *CopyScratchToListFunc = nullptr;
4926 // Thread-local storage for the reduction variables.
4927 Value *ScratchForCopyBack = nullptr;
4928 // RL pointer to which the final value from the per-thread scratch should be
4929 // copied back. (Basically RL, appropriately casted if necessary.)
4930 Value *RLForCopyBack = RL;
4931
4932 bool IsAtomicReduction =
4933 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4934
4935 if (!IsTeamsReduction) {
4936 Value *SarFuncCast =
4937 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4938 Value *WcFuncCast =
4939 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4940 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4941 WcFuncCast};
4943 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4944 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4945 } else if (IsAtomicReduction) {
4946 // Atomic cross-team reduction fast path: determine the team's main thread
4947 // that is later to fold its value atomically into the mapped variable.
4948 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4949 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4950 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4951 } else {
4952 CodeGenIP = Builder.saveIP();
4953 StructType *ReductionsBufferTy = StructType::create(
4954 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4955
4956 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4957 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4958 if (!LtGCFunc)
4959 return LtGCFunc.takeError();
4960
4961 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
4962 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4963 if (!GtLCFunc)
4964 return GtLCFunc.takeError();
4965
4966 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
4967 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
4968 if (!GtLRFunc)
4969 return GtLRFunc.takeError();
4970
4971 Builder.restoreIP(CodeGenIP);
4972
4973 // The runtime's cross-team final aggregate uses the storage pointed at by
4974 // its reduce-list argument as per-thread scratch. When the surrounding
4975 // kernel is already in SPMD execution mode, clang emitted each reduction
4976 // private as a per-thread `alloca addrspace(5)`, so the original red_list
4977 // (RL) is already per-thread and nothing else is needed.
4978 //
4979 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
4980 // Generic-mode globalization put the reduction private into team-shared
4981 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
4982 // point all threads of the last team would race on the shared LDS slot.
4983 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
4984 // value in, and hand the per-thread RL to the runtime instead. The writer
4985 // thread copies the final value from that per-thread scratch back to RL
4986 // before running the existing combine path below.
4987
4988 // Thread-local RL (might need localization below before being passed to the
4989 // runtime).
4990 Value *RuntimeRL = RL;
4991
4992 if (!IsSPMD) {
4993 CodeGenIP = Builder.saveIP();
4994 Builder.restoreIP(AllocaIP);
4995 // Allocate thread-local buffer for the reduction variables.
4996 Value *PerThreadScratchAlloca = Builder.CreateAlloca(
4997 ReductionsBufferTy, /*ArraySize=*/nullptr, ".omp.reduction.scratch");
4998 Value *PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
4999 PerThreadScratchAlloca, PtrTy,
5000 PerThreadScratchAlloca->getName() + ".ascast");
5001 // Allocate thread-local buffer for the pointers to the reduction
5002 // variables.
5003 Value *PerThreadRedListAlloca =
5004 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
5005 ".omp.reduction.per_thread_red_list");
5006 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
5007 PerThreadRedListAlloca, PtrTy,
5008 PerThreadRedListAlloca->getName() + ".ascast");
5009 Builder.restoreIP(CodeGenIP);
5010
5011 // Iterate over the reduction variables and copy the team-local value to
5012 // the thread-local buffer.
5013 for (auto En : enumerate(ReductionInfos)) {
5014 const ReductionInfo &RI = En.value();
5015 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5016
5017 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5018 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5019 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5020 0, En.index());
5021
5022 Value *RuntimeListEntry = FieldPtr;
5023 if (IsByRefElem && RI.DataPtrPtrGen) {
5024 Value *SrcDescriptor =
5025 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5026 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5027 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5028 if (!Descriptor)
5029 return Descriptor.takeError();
5030 RuntimeListEntry = *Descriptor;
5031 }
5032 Builder.CreateStore(RuntimeListEntry, Slot);
5033 }
5034 // The copy helpers were emitted with default-AS (AS 0) pointer params
5035 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5036 // but PerThreadScratch and RL live in the target's default AS, which
5037 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5038 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5039 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5040 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5041 PerThreadScratch, CopyArg0Ty);
5042 RLForCopyBack =
5043 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5044 // Use index 0 because there is no array of target values to index into,
5045 // there is only one thread-local memory slot.
5046 // restoreIP above left a stale/empty debug location; this inlinable call
5047 // to a debug-info-bearing helper needs one or the verifier rejects the
5048 // module ("!dbg attachment points at wrong subprogram") after inlining.
5049 Builder.SetCurrentDebugLocation(Loc.DL);
5050 Builder.CreateCall(
5051 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5052 CopyScratchToListFunc = *GtLCFunc;
5053 }
5054
5055 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5056 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5057
5058 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5059 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5060 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5061 }
5062
5063 // 5. Build if (res == 1)
5064 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5065 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5066 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5067 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5068
5069 // 6. Build then branch: where we have reduced values in the master
5070 // thread in each team.
5071 // __kmpc_end_reduce{_nowait}(<gtid>);
5072 // break;
5073 emitBlock(ThenBB, CurFunc);
5074
5075 // Copy the writer thread's per-thread scratch result back into the original
5076 // red-list storage before the existing combine path reads RI.PrivateVariable.
5077 // Set a debug location: this inlinable call to a debug-info-bearing helper
5078 // needs one or the verifier rejects the module after inlining.
5079 if (ScratchForCopyBack) {
5080 Builder.SetCurrentDebugLocation(Loc.DL);
5081 Builder.CreateCall(
5082 CopyScratchToListFunc,
5083 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5084 }
5085
5086 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5087 for (auto En : enumerate(ReductionInfos)) {
5088 const ReductionInfo &RI = En.value();
5089
5090 // Atomic cross-team fast path: each team's main thread folds its
5091 // team-reduced value directly into the mapped reduction variable with a
5092 // single atomicrmw.
5093 if (IsAtomicReduction) {
5095 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5096 if (!AfterIP)
5097 return AfterIP.takeError();
5098 Builder.restoreIP(*AfterIP);
5099 continue;
5100 }
5101
5103 Value *RedValue = RI.Variable;
5104
5105 Value *RHS =
5106 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5107
5109 Value *LHSPtr, *RHSPtr;
5110 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5111 &LHSPtr, &RHSPtr, CurFunc));
5112
5113 // Fix the CallBack code genereated to use the correct Values for the LHS
5114 // and RHS. Cast to match types before replacing (necessary to handle
5115 // different address spaces).
5116 if (LHSPtr->getType() != RedValue->getType())
5117 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5118 RedValue, LHSPtr->getType());
5119 if (RHSPtr->getType() != RHS->getType())
5120 RHS =
5121 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5122
5123 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5124 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5125 ReductionFunc;
5126 });
5127 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5128 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5129 ReductionFunc;
5130 });
5131 } else {
5132 if (IsByRef.empty() || !IsByRef[En.index()]) {
5133 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5134 "red.value." + Twine(En.index()));
5135 }
5136 Value *PrivateRedValue = Builder.CreateLoad(
5137 ValueType, RHS, "red.private.value" + Twine(En.index()));
5138 Value *Reduced;
5139 InsertPointOrErrorTy AfterIP =
5140 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5141 if (!AfterIP)
5142 return AfterIP.takeError();
5143 Builder.restoreIP(*AfterIP);
5144
5145 if (!IsByRef.empty() && !IsByRef[En.index()])
5146 Builder.CreateStore(Reduced, RI.Variable);
5147 }
5148 }
5149 emitBlock(ExitBB, CurFunc);
5150 if (ContinuationBlock) {
5151 Builder.CreateBr(ContinuationBlock);
5152 Builder.SetInsertPoint(ContinuationBlock);
5153 }
5154 Config.setEmitLLVMUsed();
5155
5156 return Builder.saveIP();
5157}
5158
5160 Type *VoidTy = Type::getVoidTy(M.getContext());
5161 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5162 auto *FuncTy =
5163 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5165 ".omp.reduction.func", &M);
5166}
5167
5169 Function *ReductionFunc,
5171 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5172 IRBuilder<>::InsertPointGuard IPG(Builder);
5173 Module *Module = ReductionFunc->getParent();
5174 BasicBlock *ReductionFuncBlock =
5175 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5176 Builder.SetInsertPoint(ReductionFuncBlock);
5177 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5178 Value *LHSArrayPtr = nullptr;
5179 Value *RHSArrayPtr = nullptr;
5180 if (IsGPU) {
5181 // Need to alloca memory here and deal with the pointers before getting
5182 // LHS/RHS pointers out
5183 //
5184 Argument *Arg0 = ReductionFunc->getArg(0);
5185 Argument *Arg1 = ReductionFunc->getArg(1);
5186 Type *Arg0Type = Arg0->getType();
5187 Type *Arg1Type = Arg1->getType();
5188
5189 Value *LHSAlloca =
5190 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5191 Value *RHSAlloca =
5192 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5193 Value *LHSAddrCast =
5194 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5195 Value *RHSAddrCast =
5196 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5197 Builder.CreateStore(Arg0, LHSAddrCast);
5198 Builder.CreateStore(Arg1, RHSAddrCast);
5199 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5200 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5201 } else {
5202 LHSArrayPtr = ReductionFunc->getArg(0);
5203 RHSArrayPtr = ReductionFunc->getArg(1);
5204 }
5205
5206 unsigned NumReductions = ReductionInfos.size();
5207 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5208
5209 for (auto En : enumerate(ReductionInfos)) {
5210 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5211 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5212 RedArrayTy, LHSArrayPtr, 0, En.index());
5213 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5214 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5215 LHSI8Ptr, RI.Variable->getType());
5216 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5217 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5218 RedArrayTy, RHSArrayPtr, 0, En.index());
5219 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5220 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5221 RHSI8Ptr, RI.PrivateVariable->getType());
5222 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5223 Value *Reduced;
5225 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5226 if (!AfterIP)
5227 return AfterIP.takeError();
5228
5229 Builder.restoreIP(*AfterIP);
5230 // TODO: Consider flagging an error.
5231 if (!Builder.GetInsertBlock())
5232 return Error::success();
5233
5234 // store is inside of the reduction region when using by-ref
5235 if (!IsByRef[En.index()])
5236 Builder.CreateStore(Reduced, LHSPtr);
5237 }
5238 Builder.CreateRetVoid();
5239 return Error::success();
5240}
5241
5243 const LocationDescription &Loc, InsertPointTy AllocaIP,
5244 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5245 bool IsNoWait, bool IsTeamsReduction) {
5246 assert(ReductionInfos.size() == IsByRef.size());
5247 if (Config.isGPU())
5248 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5249 IsByRef, IsNoWait, IsTeamsReduction);
5250
5251 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5252
5253 if (!updateToLocation(Loc))
5254 return InsertPointTy();
5255
5256 if (ReductionInfos.size() == 0)
5257 return Builder.saveIP();
5258
5259 BasicBlock *InsertBlock = Loc.IP.getBlock();
5260 BasicBlock *ContinuationBlock =
5261 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
5262 InsertBlock->getTerminator()->eraseFromParent();
5263
5264 // Create and populate array of type-erased pointers to private reduction
5265 // values.
5266 unsigned NumReductions = ReductionInfos.size();
5267 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5268 Builder.SetInsertPoint(AllocaIP.getBlock()->getTerminator());
5269 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5270
5271 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
5272
5273 for (auto En : enumerate(ReductionInfos)) {
5274 unsigned Index = En.index();
5275 const ReductionInfo &RI = En.value();
5276 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5277 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5278 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5279 }
5280
5281 // Emit a call to the runtime function that orchestrates the reduction.
5282 // Declare the reduction function in the process.
5283 Type *IndexTy = Builder.getIndexTy(
5284 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5285 Function *Func = Builder.GetInsertBlock()->getParent();
5286 Module *Module = Func->getParent();
5287 uint32_t SrcLocStrSize;
5288 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5289 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5290 return RI.AtomicReductionGen;
5291 });
5292 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5293 CanGenerateAtomic
5294 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5295 : IdentFlag(0));
5296 Value *ThreadId = getOrCreateThreadID(Ident);
5297 Constant *NumVariables = Builder.getInt32(NumReductions);
5298 const DataLayout &DL = Module->getDataLayout();
5299 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5300 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5301 Function *ReductionFunc = getFreshReductionFunc(*Module);
5302 Value *Lock = getOMPCriticalRegionLock(".reduction");
5304 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5305 : RuntimeFunction::OMPRTL___kmpc_reduce);
5306 CallInst *ReduceCall =
5307 createRuntimeFunctionCall(ReduceFunc,
5308 {Ident, ThreadId, NumVariables, RedArraySize,
5309 RedArray, ReductionFunc, Lock},
5310 "reduce");
5311
5312 // Create final reduction entry blocks for the atomic and non-atomic case.
5313 // Emit IR that dispatches control flow to one of the blocks based on the
5314 // reduction supporting the atomic mode.
5315 BasicBlock *NonAtomicRedBlock =
5316 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5317 BasicBlock *AtomicRedBlock =
5318 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5319 SwitchInst *Switch =
5320 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5321 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5322 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5323
5324 // Populate the non-atomic reduction using the elementwise reduction function.
5325 // This loads the elements from the global and private variables and reduces
5326 // them before storing back the result to the global variable.
5327 Builder.SetInsertPoint(NonAtomicRedBlock);
5328 for (auto En : enumerate(ReductionInfos)) {
5329 const ReductionInfo &RI = En.value();
5331 // We have one less load for by-ref case because that load is now inside of
5332 // the reduction region
5333 Value *RedValue = RI.Variable;
5334 if (!IsByRef[En.index()]) {
5335 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5336 "red.value." + Twine(En.index()));
5337 }
5338 Value *PrivateRedValue =
5339 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5340 "red.private.value." + Twine(En.index()));
5341 Value *Reduced;
5342 InsertPointOrErrorTy AfterIP =
5343 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5344 if (!AfterIP)
5345 return AfterIP.takeError();
5346 Builder.restoreIP(*AfterIP);
5347
5348 if (!Builder.GetInsertBlock())
5349 return InsertPointTy();
5350 // for by-ref case, the load is inside of the reduction region
5351 if (!IsByRef[En.index()])
5352 Builder.CreateStore(Reduced, RI.Variable);
5353 }
5354 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5355 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5356 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5357 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5358 Builder.CreateBr(ContinuationBlock);
5359
5360 // Populate the atomic reduction using the atomic elementwise reduction
5361 // function. There are no loads/stores here because they will be happening
5362 // inside the atomic elementwise reduction.
5363 Builder.SetInsertPoint(AtomicRedBlock);
5364 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5365 for (const ReductionInfo &RI : ReductionInfos) {
5367 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5368 if (!AfterIP)
5369 return AfterIP.takeError();
5370 Builder.restoreIP(*AfterIP);
5371 if (!Builder.GetInsertBlock())
5372 return InsertPointTy();
5373 }
5374 Builder.CreateBr(ContinuationBlock);
5375 } else {
5376 Builder.CreateUnreachable();
5377 }
5378
5379 // Populate the outlined reduction function using the elementwise reduction
5380 // function. Partial values are extracted from the type-erased array of
5381 // pointers to private variables.
5382 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5383 IsByRef, /*isGPU=*/false);
5384 if (Err)
5385 return Err;
5386
5387 if (!Builder.GetInsertBlock())
5388 return InsertPointTy();
5389
5390 Builder.SetInsertPoint(ContinuationBlock);
5391 return Builder.saveIP();
5392}
5393
5396 BodyGenCallbackTy BodyGenCB,
5397 FinalizeCallbackTy FiniCB) {
5398 if (!updateToLocation(Loc))
5399 return Loc.IP;
5400
5401 Directive OMPD = Directive::OMPD_master;
5402 uint32_t SrcLocStrSize;
5403 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5404 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5405 Value *ThreadId = getOrCreateThreadID(Ident);
5406 Value *Args[] = {Ident, ThreadId};
5407
5408 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5409 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5410
5411 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5412 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5413
5414 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5415 /*Conditional*/ true, /*hasFinalize*/ true);
5416}
5417
5420 BodyGenCallbackTy BodyGenCB,
5421 FinalizeCallbackTy FiniCB, Value *Filter) {
5422 if (!updateToLocation(Loc))
5423 return Loc.IP;
5424
5425 Directive OMPD = Directive::OMPD_masked;
5426 uint32_t SrcLocStrSize;
5427 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5428 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5429 Value *ThreadId = getOrCreateThreadID(Ident);
5430 Value *Args[] = {Ident, ThreadId, Filter};
5431 Value *ArgsEnd[] = {Ident, ThreadId};
5432
5433 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5434 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5435
5436 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5437 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5438
5439 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5440 /*Conditional*/ true, /*hasFinalize*/ true);
5441}
5442
5444 llvm::FunctionCallee Callee,
5446 const llvm::Twine &Name) {
5447 llvm::CallInst *Call = Builder.CreateCall(
5448 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5449 Call->setDoesNotThrow();
5450 return Call;
5451}
5452
5453// Expects input basic block is dominated by BeforeScanBB.
5454// Once Scan directive is encountered, the code after scan directive should be
5455// dominated by AfterScanBB. Scan directive splits the code sequence to
5456// scan and input phase. Based on whether inclusive or exclusive
5457// clause is used in the scan directive and whether input loop or scan loop
5458// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5459// input loop and second is the scan loop. The code generated handles only
5460// inclusive scans now.
5462 const LocationDescription &Loc, InsertPointTy AllocaIP,
5463 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5464 bool IsInclusive, ScanInfo *ScanRedInfo) {
5465 if (ScanRedInfo->OMPFirstScanLoop) {
5466 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5467 ScanVarsType, ScanRedInfo);
5468 if (Err)
5469 return Err;
5470 }
5471 if (!updateToLocation(Loc))
5472 return Loc.IP;
5473
5474 llvm::Value *IV = ScanRedInfo->IV;
5475
5476 if (ScanRedInfo->OMPFirstScanLoop) {
5477 // Emit buffer[i] = red; at the end of the input phase.
5478 for (size_t i = 0; i < ScanVars.size(); i++) {
5479 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5480 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5481 Type *DestTy = ScanVarsType[i];
5482 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5483 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5484
5485 Builder.CreateStore(Src, Val);
5486 }
5487 }
5488 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5489 emitBlock(ScanRedInfo->OMPScanDispatch,
5490 Builder.GetInsertBlock()->getParent());
5491
5492 if (!ScanRedInfo->OMPFirstScanLoop) {
5493 IV = ScanRedInfo->IV;
5494 // Emit red = buffer[i]; at the entrance to the scan phase.
5495 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5496 for (size_t i = 0; i < ScanVars.size(); i++) {
5497 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5498 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5499 Type *DestTy = ScanVarsType[i];
5500 Value *SrcPtr =
5501 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5502 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5503 Builder.CreateStore(Src, ScanVars[i]);
5504 }
5505 }
5506
5507 // TODO: Update it to CreateBr and remove dead blocks
5508 llvm::Value *CmpI = Builder.getInt1(true);
5509 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5510 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5511 ScanRedInfo->OMPAfterScanBlock);
5512 } else {
5513 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5514 ScanRedInfo->OMPBeforeScanBlock);
5515 }
5516 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5517 Builder.GetInsertBlock()->getParent());
5518 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5519 return Builder.saveIP();
5520}
5521
5522Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5523 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5524 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5525
5526 Builder.restoreIP(AllocaIP);
5527 // Create the shared pointer at alloca IP.
5528 for (size_t i = 0; i < ScanVars.size(); i++) {
5529 llvm::Value *BuffPtr =
5530 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5531 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5532 }
5533
5534 // Allocate temporary buffer by master thread
5535 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5536 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5537 Builder.restoreIP(CodeGenIP);
5538 Value *AllocSpan =
5539 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5540 for (size_t i = 0; i < ScanVars.size(); i++) {
5541 Type *IntPtrTy = Builder.getInt32Ty();
5542 Constant *Allocsize = ConstantExpr::getSizeOf(ScanVarsType[i]);
5543 Allocsize = ConstantExpr::getTruncOrBitCast(Allocsize, IntPtrTy);
5544 Value *Buff = Builder.CreateMalloc(IntPtrTy, ScanVarsType[i], Allocsize,
5545 AllocSpan, nullptr, "arr");
5546 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5547 }
5548 return Error::success();
5549 };
5550 // TODO: Perform finalization actions for variables. This has to be
5551 // called for variables which have destructors/finalizers.
5552 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5553
5554 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5555 llvm::Value *FilterVal = Builder.getInt32(0);
5557 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5558
5559 if (!AfterIP)
5560 return AfterIP.takeError();
5561 Builder.restoreIP(*AfterIP);
5562 BasicBlock *InputBB = Builder.GetInsertBlock();
5563 if (InputBB->hasTerminator())
5564 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
5565 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5566 if (!AfterIP)
5567 return AfterIP.takeError();
5568 Builder.restoreIP(*AfterIP);
5569
5570 return Error::success();
5571}
5572
5573Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5574 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5575 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5576 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5577 Builder.restoreIP(CodeGenIP);
5578 for (ReductionInfo RedInfo : ReductionInfos) {
5579 Value *PrivateVar = RedInfo.PrivateVariable;
5580 Value *OrigVar = RedInfo.Variable;
5581 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5582 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5583
5584 Type *SrcTy = RedInfo.ElementType;
5585 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5586 "arrayOffset");
5587 Value *Src = Builder.CreateLoad(SrcTy, Val);
5588
5589 Builder.CreateStore(Src, OrigVar);
5590 Builder.CreateFree(Buff);
5591 }
5592 return Error::success();
5593 };
5594 // TODO: Perform finalization actions for variables. This has to be
5595 // called for variables which have destructors/finalizers.
5596 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5597
5598 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5599 Builder.SetInsertPoint(TI);
5600 else
5601 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5602
5603 llvm::Value *FilterVal = Builder.getInt32(0);
5605 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5606
5607 if (!AfterIP)
5608 return AfterIP.takeError();
5609 Builder.restoreIP(*AfterIP);
5610 BasicBlock *InputBB = Builder.GetInsertBlock();
5611 if (InputBB->hasTerminator())
5612 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
5613 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5614 if (!AfterIP)
5615 return AfterIP.takeError();
5616 Builder.restoreIP(*AfterIP);
5617 return Error::success();
5618}
5619
5621 const LocationDescription &Loc,
5623 ScanInfo *ScanRedInfo) {
5624
5625 if (!updateToLocation(Loc))
5626 return Loc.IP;
5627 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5628 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5629 Builder.restoreIP(CodeGenIP);
5630 Function *CurFn = Builder.GetInsertBlock()->getParent();
5631 // for (int k = 0; k <= ceil(log2(n)); ++k)
5632 llvm::BasicBlock *LoopBB =
5633 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5634 llvm::BasicBlock *ExitBB =
5635 splitBB(Builder, false, "omp.outer.log.scan.exit");
5637 Builder.GetInsertBlock()->getModule(),
5638 (llvm::Intrinsic::ID)llvm::Intrinsic::log2, Builder.getDoubleTy());
5639 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5640 llvm::Value *Arg =
5641 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5642 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5644 Builder.GetInsertBlock()->getModule(),
5645 (llvm::Intrinsic::ID)llvm::Intrinsic::ceil, Builder.getDoubleTy());
5646 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5647 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5648 llvm::Value *NMin1 = Builder.CreateNUWSub(
5649 ScanRedInfo->Span,
5650 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5651 Builder.SetInsertPoint(InputBB);
5652 Builder.CreateBr(LoopBB);
5653 emitBlock(LoopBB, CurFn);
5654 Builder.SetInsertPoint(LoopBB);
5655
5656 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5657 // size pow2k = 1;
5658 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5659 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5660 InputBB);
5661 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5662 InputBB);
5663 // for (size i = n - 1; i >= 2 ^ k; --i)
5664 // tmp[i] op= tmp[i-pow2k];
5665 llvm::BasicBlock *InnerLoopBB =
5666 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5667 llvm::BasicBlock *InnerExitBB =
5668 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5669 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5670 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5671 emitBlock(InnerLoopBB, CurFn);
5672 Builder.SetInsertPoint(InnerLoopBB);
5673 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5674 IVal->addIncoming(NMin1, LoopBB);
5675 for (ReductionInfo RedInfo : ReductionInfos) {
5676 Value *ReductionVal = RedInfo.PrivateVariable;
5677 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5678 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5679 Type *DestTy = RedInfo.ElementType;
5680 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5681 Value *LHSPtr =
5682 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5683 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5684 Value *RHSPtr =
5685 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5686 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5687 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5688 llvm::Value *Result;
5689 InsertPointOrErrorTy AfterIP =
5690 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5691 if (!AfterIP)
5692 return AfterIP.takeError();
5693 Builder.CreateStore(Result, LHSPtr);
5694 }
5695 llvm::Value *NextIVal = Builder.CreateNUWSub(
5696 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5697 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5698 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5699 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5700 emitBlock(InnerExitBB, CurFn);
5701 llvm::Value *Next = Builder.CreateNUWAdd(
5702 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5703 Counter->addIncoming(Next, Builder.GetInsertBlock());
5704 // pow2k <<= 1;
5705 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5706 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5707 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5708 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5709 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5710 return Error::success();
5711 };
5712
5713 // TODO: Perform finalization actions for variables. This has to be
5714 // called for variables which have destructors/finalizers.
5715 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5716
5717 llvm::Value *FilterVal = Builder.getInt32(0);
5719 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5720
5721 if (!AfterIP)
5722 return AfterIP.takeError();
5723 Builder.restoreIP(*AfterIP);
5724 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5725
5726 if (!AfterIP)
5727 return AfterIP.takeError();
5728 Builder.restoreIP(*AfterIP);
5729 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5730 if (Err)
5731 return Err;
5732
5733 return AfterIP;
5734}
5735
5736Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5737 llvm::function_ref<Error()> InputLoopGen,
5738 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5739 ScanInfo *ScanRedInfo) {
5740
5741 {
5742 // Emit loop with input phase:
5743 // for (i: 0..<num_iters>) {
5744 // <input phase>;
5745 // buffer[i] = red;
5746 // }
5747 ScanRedInfo->OMPFirstScanLoop = true;
5748 Error Err = InputLoopGen();
5749 if (Err)
5750 return Err;
5751 }
5752 {
5753 // Emit loop with scan phase:
5754 // for (i: 0..<num_iters>) {
5755 // red = buffer[i];
5756 // <scan phase>;
5757 // }
5758 ScanRedInfo->OMPFirstScanLoop = false;
5759 Error Err = ScanLoopGen(Builder.saveIP());
5760 if (Err)
5761 return Err;
5762 }
5763 return Error::success();
5764}
5765
5766void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5767 Function *Fun = Builder.GetInsertBlock()->getParent();
5768 ScanRedInfo->OMPScanDispatch =
5769 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5770 ScanRedInfo->OMPAfterScanBlock =
5771 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5772 ScanRedInfo->OMPBeforeScanBlock =
5773 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5774 ScanRedInfo->OMPScanLoopExit =
5775 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5776}
5778 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5779 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5780 Module *M = F->getParent();
5781 LLVMContext &Ctx = M->getContext();
5782 Type *IndVarTy = TripCount->getType();
5783
5784 // Create the basic block structure.
5785 BasicBlock *Preheader =
5786 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5787 BasicBlock *Header =
5788 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5789 BasicBlock *Cond =
5790 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5791 BasicBlock *Body =
5792 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5793 BasicBlock *Latch =
5794 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5795 BasicBlock *Exit =
5796 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5797 BasicBlock *After =
5798 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5799
5800 // Use specified DebugLoc for new instructions.
5801 Builder.SetCurrentDebugLocation(DL);
5802
5803 Builder.SetInsertPoint(Preheader);
5804 Builder.CreateBr(Header);
5805
5806 Builder.SetInsertPoint(Header);
5807 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5808 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5809 Builder.CreateBr(Cond);
5810
5811 Builder.SetInsertPoint(Cond);
5812 Value *Cmp =
5813 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5814 Builder.CreateCondBr(Cmp, Body, Exit);
5815
5816 Builder.SetInsertPoint(Body);
5817 Builder.CreateBr(Latch);
5818
5819 Builder.SetInsertPoint(Latch);
5820 // Decide whether the induction variable increment can carry nsw.
5821 //
5822 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5823 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5824 // for valid programs 0 <= count <= INT_MAX always holds.
5825 //
5826 // Collapsed loops: the trip count is a product that can overflow i32 even for
5827 // a conforming program, so nsw is kept only when the product is a constant
5828 // that provably fits, dropped otherwise.
5829 bool HasNSW = Config.hasNoSignedWrap();
5830 if (HasNSW) {
5831 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5832 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5834 if (CI->getValue().ugt(SignedMax))
5835 HasNSW = false;
5836 } else if (IsCollapsed) {
5837 HasNSW = false;
5838 }
5839 }
5840 Value *Next =
5841 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5842 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5843 Builder.CreateBr(Header);
5844 IndVarPHI->addIncoming(Next, Latch);
5845
5846 Builder.SetInsertPoint(Exit);
5847 Builder.CreateBr(After);
5848
5849 // Remember and return the canonical control flow.
5850 LoopInfos.emplace_front();
5851 CanonicalLoopInfo *CL = &LoopInfos.front();
5852
5853 CL->Header = Header;
5854 CL->Cond = Cond;
5855 CL->Latch = Latch;
5856 CL->Exit = Exit;
5857
5858#ifndef NDEBUG
5859 CL->assertOK();
5860#endif
5861 return CL;
5862}
5863
5866 LoopBodyGenCallbackTy BodyGenCB,
5867 Value *TripCount, const Twine &Name) {
5868 BasicBlock *BB = Loc.IP.getBlock();
5869 BasicBlock *NextBB = BB->getNextNode();
5870
5871 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5872 NextBB, NextBB, Name);
5873 BasicBlock *After = CL->getAfter();
5874
5875 // If location is not set, don't connect the loop.
5876 if (updateToLocation(Loc)) {
5877 // Split the loop at the insertion point: Branch to the preheader and move
5878 // every following instruction to after the loop (the After BB). Also, the
5879 // new successor is the loop's after block.
5880 spliceBB(Builder, After, /*CreateBranch=*/false);
5881 Builder.CreateBr(CL->getPreheader());
5882 }
5883
5884 // Emit the body content. We do it after connecting the loop to the CFG to
5885 // avoid that the callback encounters degenerate BBs.
5886 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5887 return Err;
5888
5889#ifndef NDEBUG
5890 CL->assertOK();
5891#endif
5892 return CL;
5893}
5894
5896 ScanInfos.emplace_front();
5897 ScanInfo *Result = &ScanInfos.front();
5898 return Result;
5899}
5900
5904 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5905 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5906 LocationDescription ComputeLoc =
5907 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5908 updateToLocation(ComputeLoc);
5909
5911
5913 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5914 ScanRedInfo->Span = TripCount;
5915 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5916 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5917
5918 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5919 Builder.restoreIP(CodeGenIP);
5920 ScanRedInfo->IV = IV;
5921 createScanBBs(ScanRedInfo);
5922 BasicBlock *InputBlock = Builder.GetInsertBlock();
5923 Instruction *Terminator = InputBlock->getTerminator();
5924 assert(Terminator->getNumSuccessors() == 1);
5925 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5926 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5927 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5928 Builder.GetInsertBlock()->getParent());
5929 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5930 emitBlock(ScanRedInfo->OMPScanLoopExit,
5931 Builder.GetInsertBlock()->getParent());
5932 Builder.CreateBr(ContinueBlock);
5933 Builder.SetInsertPoint(
5934 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5935 return BodyGenCB(Builder.saveIP(), IV);
5936 };
5937
5938 const auto &&InputLoopGen = [&]() -> Error {
5940 Builder.saveIP(), BodyGen, Start, Stop, Step, IsSigned, InclusiveStop,
5941 ComputeIP, Name, true, ScanRedInfo);
5942 if (!LoopInfo)
5943 return LoopInfo.takeError();
5944 Result.push_back(*LoopInfo);
5945 Builder.restoreIP((*LoopInfo)->getAfterIP());
5946 return Error::success();
5947 };
5948 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5950 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5951 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5952 if (!LoopInfo)
5953 return LoopInfo.takeError();
5954 Result.push_back(*LoopInfo);
5955 Builder.restoreIP((*LoopInfo)->getAfterIP());
5956 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5957 return Error::success();
5958 };
5959 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5960 if (Err)
5961 return Err;
5962 return Result;
5963}
5964
5966 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5967 bool IsSigned, bool InclusiveStop, const Twine &Name) {
5968
5969 // Consider the following difficulties (assuming 8-bit signed integers):
5970 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
5971 // DO I = 1, 100, 50
5972 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
5973 // DO I = 100, 0, -128
5974
5975 // Start, Stop and Step must be of the same integer type.
5976 auto *IndVarTy = cast<IntegerType>(Start->getType());
5977 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
5978 assert(IndVarTy == Step->getType() && "Step type mismatch");
5979
5981
5982 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
5983 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
5984
5985 // Like Step, but always positive.
5986 Value *Incr = Step;
5987
5988 // Distance between Start and Stop; always positive.
5989 Value *Span;
5990
5991 // Condition whether there are no iterations are executed at all, e.g. because
5992 // UB < LB.
5993 Value *ZeroCmp;
5994
5995 if (IsSigned) {
5996 // Ensure that increment is positive. If not, negate and invert LB and UB.
5997 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
5998 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
5999 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6000 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6001 Span = Builder.CreateSub(UB, LB, "", false, true);
6002 ZeroCmp = Builder.CreateICmp(
6003 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6004 } else {
6005 Span = Builder.CreateSub(Stop, Start, "", true);
6006 ZeroCmp = Builder.CreateICmp(
6007 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6008 }
6009
6010 Value *CountIfLooping;
6011 if (InclusiveStop) {
6012 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6013 } else {
6014 // Avoid incrementing past stop since it could overflow.
6015 Value *CountIfTwo = Builder.CreateAdd(
6016 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6017 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6018 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6019 }
6020
6021 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6022 "omp_" + Name + ".tripcount");
6023}
6024
6027 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6028 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6029 ScanInfo *ScanRedInfo) {
6030 LocationDescription ComputeLoc =
6031 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6032
6034 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6035
6036 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6037 Builder.restoreIP(CodeGenIP);
6038 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6039 /*HasNSW=*/Config.hasNoSignedWrap());
6040 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6041 /*HasNSW=*/Config.hasNoSignedWrap());
6042 if (InScan)
6043 ScanRedInfo->IV = IndVar;
6044 return BodyGenCB(Builder.saveIP(), IndVar);
6045 };
6046 LocationDescription LoopLoc =
6047 ComputeIP.isSet()
6048 ? Loc
6049 : LocationDescription(Builder.saveIP(),
6050 Builder.getCurrentDebugLocation());
6051 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6052}
6053
6054// Returns an LLVM function to call for initializing loop bounds using OpenMP
6055// static scheduling for composite `distribute parallel for` depending on
6056// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6057// integers as unsigned similarly to CanonicalLoopInfo.
6058static FunctionCallee
6060 OpenMPIRBuilder &OMPBuilder) {
6061 unsigned Bitwidth = Ty->getIntegerBitWidth();
6062 if (Bitwidth == 32)
6063 return OMPBuilder.getOrCreateRuntimeFunction(
6064 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6065 if (Bitwidth == 64)
6066 return OMPBuilder.getOrCreateRuntimeFunction(
6067 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6068 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6069}
6070
6071// Returns an LLVM function to call for initializing loop bounds using OpenMP
6072// static scheduling depending on `type`. Only i32 and i64 are supported by the
6073// runtime. Always interpret integers as unsigned similarly to
6074// CanonicalLoopInfo.
6076 OpenMPIRBuilder &OMPBuilder) {
6077 unsigned Bitwidth = Ty->getIntegerBitWidth();
6078 if (Bitwidth == 32)
6079 return OMPBuilder.getOrCreateRuntimeFunction(
6080 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6081 if (Bitwidth == 64)
6082 return OMPBuilder.getOrCreateRuntimeFunction(
6083 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6084 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6085}
6086
6087OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6088 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6089 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6090 OMPScheduleType DistScheduleSchedType) {
6091 assert(CLI->isValid() && "Requires a valid canonical loop");
6092 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6093 "Require dedicated allocate IP");
6094
6095 // Set up the source location value for OpenMP runtime.
6096 Builder.restoreIP(CLI->getPreheaderIP());
6097 Builder.SetCurrentDebugLocation(DL);
6098
6099 uint32_t SrcLocStrSize;
6100 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6102 switch (LoopType) {
6103 case WorksharingLoopType::ForStaticLoop:
6104 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6105 break;
6106 case WorksharingLoopType::DistributeStaticLoop:
6107 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6108 break;
6109 case WorksharingLoopType::DistributeForStaticLoop:
6110 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6111 break;
6112 }
6113 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6114
6115 // Declare useful OpenMP runtime functions.
6116 Value *IV = CLI->getIndVar();
6117 Type *IVTy = IV->getType();
6118 FunctionCallee StaticInit =
6119 LoopType == WorksharingLoopType::DistributeForStaticLoop
6120 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6121 : getKmpcForStaticInitForType(IVTy, M, *this);
6122 FunctionCallee StaticFini =
6123 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6124
6125 // Allocate space for computed loop bounds as expected by the "init" function.
6126 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6127
6128 Type *I32Type = Type::getInt32Ty(M.getContext());
6129 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6130 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6131 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6132 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6133 CLI->setLastIter(PLastIter);
6134
6135 // At the end of the preheader, prepare for calling the "init" function by
6136 // storing the current loop bounds into the allocated space. A canonical loop
6137 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6138 // and produces an inclusive upper bound.
6139 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6140 Constant *Zero = ConstantInt::get(IVTy, 0);
6141 Constant *One = ConstantInt::get(IVTy, 1);
6142 Builder.CreateStore(Zero, PLowerBound);
6143 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6144 Builder.CreateStore(UpperBound, PUpperBound);
6145 Builder.CreateStore(One, PStride);
6146
6147 Value *ThreadNum =
6148 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6149
6150 OMPScheduleType SchedType =
6151 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6152 ? OMPScheduleType::OrderedDistribute
6154 Constant *SchedulingType =
6155 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6156
6157 // Call the "init" function and update the trip count of the loop with the
6158 // value it produced.
6159 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6160 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6161 this](Value *SchedulingType, auto &Builder) {
6162 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6163 PLowerBound, PUpperBound});
6164 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6165 Value *PDistUpperBound =
6166 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6167 Args.push_back(PDistUpperBound);
6168 }
6169 Args.append({PStride, One, Zero});
6170 createRuntimeFunctionCall(StaticInit, Args);
6171 };
6172 BuildInitCall(SchedulingType, Builder);
6173 if (HasDistSchedule &&
6174 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6175 Constant *DistScheduleSchedType = ConstantInt::get(
6176 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6177 // We want to emit a second init function call for the dist_schedule clause
6178 // to the Distribute construct. This should only be done however if a
6179 // Workshare Loop is nested within a Distribute Construct
6180 BuildInitCall(DistScheduleSchedType, Builder);
6181 }
6182 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6183 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6184 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6185 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6186 CLI->setTripCount(TripCount);
6187
6188 // Update all uses of the induction variable except the one in the condition
6189 // block that compares it with the actual upper bound, and the increment in
6190 // the latch block.
6191
6192 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6193 Builder.SetInsertPoint(CLI->getBody(),
6194 CLI->getBody()->getFirstInsertionPt());
6195 Builder.SetCurrentDebugLocation(DL);
6196 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6197 /*HasNSW=*/Config.hasNoSignedWrap());
6198 });
6199
6200 // In the "exit" block, call the "fini" function.
6201 Builder.SetInsertPoint(CLI->getExit(),
6202 CLI->getExit()->getTerminator()->getIterator());
6203 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6204
6205 // Add the barrier if requested.
6206 if (NeedsBarrier) {
6207 InsertPointOrErrorTy BarrierIP =
6209 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6210 /* CheckCancelFlag */ false);
6211 if (!BarrierIP)
6212 return BarrierIP.takeError();
6213 }
6214
6215 InsertPointTy AfterIP = CLI->getAfterIP();
6216 CLI->invalidate();
6217
6218 return AfterIP;
6219}
6220
6221static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6222 LoopInfo &LI);
6223static void addLoopMetadata(CanonicalLoopInfo *Loop,
6224 ArrayRef<Metadata *> Properties);
6225
6227 LLVMContext &Ctx, Loop *Loop,
6229 SmallVector<Metadata *> &LoopMDList) {
6230 SmallSet<BasicBlock *, 8> Reachable;
6231
6232 // Get the basic blocks from the loop in which memref instructions
6233 // can be found.
6234 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6235 // preferably without running any passes.
6236 for (BasicBlock *Block : Loop->getBlocks()) {
6237 if (Block == CLI->getCond() || Block == CLI->getHeader())
6238 continue;
6239 Reachable.insert(Block);
6240 }
6241
6242 // Add access group metadata to memory-access instructions.
6243 MDNode *AccessGroup = MDNode::getDistinct(Ctx, {});
6244 for (BasicBlock *BB : Reachable)
6245 addAccessGroupMetadata(BB, AccessGroup, LoopInfo);
6246 // TODO: If the loop has existing parallel access metadata, have
6247 // to combine two lists.
6248 LoopMDList.push_back(MDNode::get(
6249 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6250}
6251
6253OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6254 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6255 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6256 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6257 assert(CLI->isValid() && "Requires a valid canonical loop");
6258 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6259
6260 LLVMContext &Ctx = CLI->getFunction()->getContext();
6261 Value *IV = CLI->getIndVar();
6262 Value *OrigTripCount = CLI->getTripCount();
6263 Type *IVTy = IV->getType();
6264 assert(IVTy->getIntegerBitWidth() <= 64 &&
6265 "Max supported tripcount bitwidth is 64 bits");
6266 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6267 : Type::getInt64Ty(Ctx);
6268 Type *I32Type = Type::getInt32Ty(M.getContext());
6269 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6270 Constant *One = ConstantInt::get(InternalIVTy, 1);
6271
6272 Function *F = CLI->getFunction();
6273 // Blocks must have terminators.
6274 // FIXME: Don't run analyses on incomplete/invalid IR.
6275 SmallVector<Instruction *> UIs;
6276 for (BasicBlock &BB : *F)
6277 if (!BB.hasTerminator())
6278 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6280 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6281 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6282 LoopAnalysis LIA;
6283 LoopInfo &&LI = LIA.run(*F, FAM);
6284 for (Instruction *I : UIs)
6285 I->eraseFromParent();
6286 Loop *L = LI.getLoopFor(CLI->getHeader());
6287 SmallVector<Metadata *> LoopMDList;
6288 if (ChunkSize || DistScheduleChunkSize)
6289 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6290 addLoopMetadata(CLI, LoopMDList);
6291
6292 // Declare useful OpenMP runtime functions.
6293 FunctionCallee StaticInit =
6294 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6295 FunctionCallee StaticFini =
6296 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6297
6298 // Allocate space for computed loop bounds as expected by the "init" function.
6299 Builder.restoreIP(AllocaIP);
6300 Builder.SetCurrentDebugLocation(DL);
6301 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6302 Value *PLowerBound =
6303 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6304 Value *PUpperBound =
6305 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6306 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6307 CLI->setLastIter(PLastIter);
6308
6309 // Set up the source location value for the OpenMP runtime.
6310 Builder.restoreIP(CLI->getPreheaderIP());
6311 Builder.SetCurrentDebugLocation(DL);
6312
6313 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6314 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6315 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6316 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6317 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6318 "distschedulechunksize");
6319 Value *CastedTripCount =
6320 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6321
6322 Constant *SchedulingType =
6323 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6324 Constant *DistSchedulingType =
6325 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6326 Builder.CreateStore(Zero, PLowerBound);
6327 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6328 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6329 Value *UpperBound =
6330 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6331 Builder.CreateStore(UpperBound, PUpperBound);
6332 Builder.CreateStore(One, PStride);
6333
6334 // Call the "init" function and update the trip count of the loop with the
6335 // value it produced.
6336 uint32_t SrcLocStrSize;
6337 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6338 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6339 if (DistScheduleSchedType != OMPScheduleType::None) {
6340 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6341 }
6342 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6343 Value *ThreadNum =
6344 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6345 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6346 PUpperBound, PStride, One,
6347 this](Value *SchedulingType, Value *ChunkSize,
6348 auto &Builder) {
6350 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6351 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6352 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6353 /*pstride=*/PStride, /*incr=*/One,
6354 /*chunk=*/ChunkSize});
6355 };
6356 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6357 if (DistScheduleSchedType != OMPScheduleType::None &&
6358 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6359 SchedType != OMPScheduleType::OrderedDistribute) {
6360 // We want to emit a second init function call for the dist_schedule clause
6361 // to the Distribute construct. This should only be done however if a
6362 // Workshare Loop is nested within a Distribute Construct
6363 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6364 }
6365
6366 // Load values written by the "init" function.
6367 Value *FirstChunkStart =
6368 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6369 Value *FirstChunkStop =
6370 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6371 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6372 Value *ChunkRange =
6373 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6374 Value *NextChunkStride =
6375 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6376
6377 // Create outer "dispatch" loop for enumerating the chunks.
6378 BasicBlock *DispatchEnter = splitBB(Builder, true);
6379 Value *DispatchCounter;
6380
6381 // It is safe to assume this didn't return an error because the callback
6382 // passed into createCanonicalLoop is the only possible error source, and it
6383 // always returns success.
6384 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6385 {Builder.saveIP(), DL},
6386 [&](InsertPointTy BodyIP, Value *Counter) {
6387 DispatchCounter = Counter;
6388 return Error::success();
6389 },
6390 FirstChunkStart, CastedTripCount, NextChunkStride,
6391 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6392 "dispatch"));
6393
6394 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6395 // not have to preserve the canonical invariant.
6396 BasicBlock *DispatchBody = DispatchCLI->getBody();
6397 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6398 BasicBlock *DispatchExit = DispatchCLI->getExit();
6399 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6400 DispatchCLI->invalidate();
6401
6402 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6403 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6404 redirectTo(CLI->getExit(), DispatchLatch, DL);
6405 redirectTo(DispatchBody, DispatchEnter, DL);
6406
6407 // Prepare the prolog of the chunk loop.
6408 Builder.restoreIP(CLI->getPreheaderIP());
6409 Builder.SetCurrentDebugLocation(DL);
6410
6411 // Compute the number of iterations of the chunk loop.
6412 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6413 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6414 Value *IsLastChunk =
6415 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6416 Value *CountUntilOrigTripCount =
6417 Builder.CreateSub(CastedTripCount, DispatchCounter);
6418 Value *ChunkTripCount = Builder.CreateSelect(
6419 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6420 Value *BackcastedChunkTC =
6421 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6422 CLI->setTripCount(BackcastedChunkTC);
6423
6424 // Update all uses of the induction variable except the one in the condition
6425 // block that compares it with the actual upper bound, and the increment in
6426 // the latch block.
6427 Value *BackcastedDispatchCounter =
6428 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6429 CLI->mapIndVar([&](Instruction *) -> Value * {
6430 Builder.restoreIP(CLI->getBodyIP());
6431 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6432 });
6433
6434 // In the "exit" block, call the "fini" function.
6435 Builder.SetInsertPoint(DispatchExit, DispatchExit->getFirstInsertionPt());
6436 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6437
6438 // Add the barrier if requested.
6439 if (NeedsBarrier) {
6440 InsertPointOrErrorTy AfterIP =
6441 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6442 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6443 if (!AfterIP)
6444 return AfterIP.takeError();
6445 }
6446
6447#ifndef NDEBUG
6448 // Even though we currently do not support applying additional methods to it,
6449 // the chunk loop should remain a canonical loop.
6450 CLI->assertOK();
6451#endif
6452
6453 return InsertPointTy(DispatchAfter, DispatchAfter->getFirstInsertionPt());
6454}
6455
6456// Returns an LLVM function to call for executing an OpenMP static worksharing
6457// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6458// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6459static FunctionCallee
6461 WorksharingLoopType LoopType) {
6462 unsigned Bitwidth = Ty->getIntegerBitWidth();
6463 Module &M = OMPBuilder->M;
6464 switch (LoopType) {
6465 case WorksharingLoopType::ForStaticLoop:
6466 if (Bitwidth == 32)
6467 return OMPBuilder->getOrCreateRuntimeFunction(
6468 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6469 if (Bitwidth == 64)
6470 return OMPBuilder->getOrCreateRuntimeFunction(
6471 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6472 break;
6473 case WorksharingLoopType::DistributeStaticLoop:
6474 if (Bitwidth == 32)
6475 return OMPBuilder->getOrCreateRuntimeFunction(
6476 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6477 if (Bitwidth == 64)
6478 return OMPBuilder->getOrCreateRuntimeFunction(
6479 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6480 break;
6481 case WorksharingLoopType::DistributeForStaticLoop:
6482 if (Bitwidth == 32)
6483 return OMPBuilder->getOrCreateRuntimeFunction(
6484 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6485 if (Bitwidth == 64)
6486 return OMPBuilder->getOrCreateRuntimeFunction(
6487 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6488 break;
6489 }
6490 if (Bitwidth != 32 && Bitwidth != 64) {
6491 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6492 }
6493 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6494}
6495
6496// Inserts a call to proper OpenMP Device RTL function which handles
6497// loop worksharing.
6499 WorksharingLoopType LoopType,
6500 BasicBlock *InsertBlock, Value *Ident,
6501 Value *LoopBodyArg, Value *TripCount,
6502 Function &LoopBodyFn, bool NoLoop) {
6503 Type *TripCountTy = TripCount->getType();
6504 Module &M = OMPBuilder->M;
6505 IRBuilder<> &Builder = OMPBuilder->Builder;
6506 FunctionCallee RTLFn =
6507 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6508 SmallVector<Value *, 8> RealArgs;
6509 RealArgs.push_back(Ident);
6510 RealArgs.push_back(&LoopBodyFn);
6511 RealArgs.push_back(LoopBodyArg);
6512 RealArgs.push_back(TripCount);
6513 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6514 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6515 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6516 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6517 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6518 return;
6519 }
6520 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6521 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6522 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6523 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6524
6525 RealArgs.push_back(
6526 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6527 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6528 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6529 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6530 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6531 } else {
6532 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6533 }
6534
6535 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6536}
6537
6539 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6540 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6541 WorksharingLoopType LoopType, bool NoLoop) {
6542 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6543 BasicBlock *Preheader = CLI->getPreheader();
6544 Value *TripCount = CLI->getTripCount();
6545
6546 // After loop body outling, the loop body contains only set up
6547 // of loop body argument structure and the call to the outlined
6548 // loop body function. Firstly, we need to move setup of loop body args
6549 // into loop preheader.
6550 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6551 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6552
6553 // The next step is to remove the whole loop. We do not it need anymore.
6554 // That's why make an unconditional branch from loop preheader to loop
6555 // exit block
6556 Builder.restoreIP({Preheader, Preheader->end()});
6557 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6558 Preheader->getTerminator()->eraseFromParent();
6559 Builder.CreateBr(CLI->getExit());
6560
6561 // Delete dead loop blocks
6562 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6563 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6564 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6565 CleanUpInfo.EntryBB = CLI->getHeader();
6566 CleanUpInfo.ExitBB = CLI->getExit();
6567 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6568 DeleteDeadBlocks(BlocksToBeRemoved);
6569
6570 // Find the instruction which corresponds to loop body argument structure
6571 // and remove the call to loop body function instruction.
6572 Value *LoopBodyArg;
6573 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6574 assert(OutlinedFnUser &&
6575 "Expected unique undroppable user of outlined function");
6576 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6577 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6578 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6579 "Expected outlined function call to be located in loop preheader");
6580 // Check in case no argument structure has been passed.
6581 if (OutlinedFnCallInstruction->arg_size() > 1)
6582 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6583 else
6584 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6585 OutlinedFnCallInstruction->eraseFromParent();
6586
6587 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6588 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6589
6590 for (auto &ToBeDeletedItem : ToBeDeleted)
6591 ToBeDeletedItem->eraseFromParent();
6592 CLI->invalidate();
6593}
6594
6595OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6596 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6597 WorksharingLoopType LoopType, bool NoLoop) {
6598 uint32_t SrcLocStrSize;
6599 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6601 switch (LoopType) {
6602 case WorksharingLoopType::ForStaticLoop:
6603 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6604 break;
6605 case WorksharingLoopType::DistributeStaticLoop:
6606 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6607 break;
6608 case WorksharingLoopType::DistributeForStaticLoop:
6609 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6610 break;
6611 }
6612 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6613
6614 auto OI = std::make_unique<OutlineInfo>();
6615 OI->OuterAllocBB = CLI->getPreheader();
6616 Function *OuterFn = CLI->getPreheader()->getParent();
6617
6618 // Instructions which need to be deleted at the end of code generation
6619 SmallVector<Instruction *, 4> ToBeDeleted;
6620
6621 OI->OuterAllocBB = AllocaIP.getBlock();
6622
6623 // Mark the body loop as region which needs to be extracted
6624 OI->EntryBB = CLI->getBody();
6625 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6626 "omp.prelatch");
6627
6628 // Prepare loop body for extraction
6629 Builder.restoreIP({CLI->getPreheader(), CLI->getPreheader()->begin()});
6630
6631 // Insert new loop counter variable which will be used only in loop
6632 // body.
6633 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6634 Instruction *NewLoopCntLoad =
6635 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6636 // New loop counter instructions are redundant in the loop preheader when
6637 // code generation for workshare loop is finshed. That's why mark them as
6638 // ready for deletion.
6639 ToBeDeleted.push_back(NewLoopCntLoad);
6640 ToBeDeleted.push_back(NewLoopCnt);
6641
6642 // Analyse loop body region. Find all input variables which are used inside
6643 // loop body region.
6644 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6646 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6647
6648 CodeExtractorAnalysisCache CEAC(*OuterFn);
6649 CodeExtractor Extractor(Blocks,
6650 /* DominatorTree */ nullptr,
6651 /* AggregateArgs */ true,
6652 /* BlockFrequencyInfo */ nullptr,
6653 /* BranchProbabilityInfo */ nullptr,
6654 /* AssumptionCache */ nullptr,
6655 /* AllowVarArgs */ true,
6656 /* AllowAlloca */ true,
6657 /* AllocationBlock */ CLI->getPreheader(),
6658 /* DeallocationBlocks */ {},
6659 /* Suffix */ ".omp_wsloop",
6660 /* AggrArgsIn0AddrSpace */ true);
6661
6662 BasicBlock *CommonExit = nullptr;
6663 SetVector<Value *> SinkingCands, HoistingCands;
6664
6665 // Find allocas outside the loop body region which are used inside loop
6666 // body
6667 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6668
6669 // We need to model loop body region as the function f(cnt, loop_arg).
6670 // That's why we replace loop induction variable by the new counter
6671 // which will be one of loop body function argument
6673 CLI->getIndVar()->user_end());
6674 for (auto Use : Users) {
6675 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6676 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6677 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6678 }
6679 }
6680 }
6681 // Make sure that loop counter variable is not merged into loop body
6682 // function argument structure and it is passed as separate variable
6683 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6684
6685 // PostOutline CB is invoked when loop body function is outlined and
6686 // loop body is replaced by call to outlined function. We need to add
6687 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6688 // function will handle loop control logic.
6689 //
6690 OI->PostOutlineCB = [=, ToBeDeletedVec =
6691 std::move(ToBeDeleted)](Function &OutlinedFn) {
6692 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6693 LoopType, NoLoop);
6694 };
6695 addOutlineInfo(std::move(OI));
6696 return CLI->getAfterIP();
6697}
6698
6701 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6702 bool HasSimdModifier, bool HasMonotonicModifier,
6703 bool HasNonmonotonicModifier, bool HasOrderedClause,
6704 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6705 Value *DistScheduleChunkSize) {
6706 if (Config.isTargetDevice())
6707 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NoLoop);
6708 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6709 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6710 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6711
6712 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6713 OMPScheduleType::ModifierOrdered;
6714 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6715 if (HasDistSchedule) {
6716 DistScheduleSchedType = DistScheduleChunkSize
6717 ? OMPScheduleType::OrderedDistributeChunked
6718 : OMPScheduleType::OrderedDistribute;
6719 }
6720 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6721 case OMPScheduleType::BaseStatic:
6722 case OMPScheduleType::BaseDistribute:
6723 assert((!ChunkSize || !DistScheduleChunkSize) &&
6724 "No chunk size with static-chunked schedule");
6725 if (IsOrdered && !HasDistSchedule)
6726 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6727 NeedsBarrier, ChunkSize);
6728 // FIXME: Monotonicity ignored?
6729 if (DistScheduleChunkSize)
6730 return applyStaticChunkedWorkshareLoop(
6731 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6732 DistScheduleChunkSize, DistScheduleSchedType);
6733 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6734 HasDistSchedule);
6735
6736 case OMPScheduleType::BaseStaticChunked:
6737 case OMPScheduleType::BaseDistributeChunked:
6738 if (IsOrdered && !HasDistSchedule)
6739 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6740 NeedsBarrier, ChunkSize);
6741 // FIXME: Monotonicity ignored?
6742 return applyStaticChunkedWorkshareLoop(
6743 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6744 DistScheduleChunkSize, DistScheduleSchedType);
6745
6746 case OMPScheduleType::BaseRuntime:
6747 case OMPScheduleType::BaseAuto:
6748 case OMPScheduleType::BaseGreedy:
6749 case OMPScheduleType::BaseBalanced:
6750 case OMPScheduleType::BaseSteal:
6751 case OMPScheduleType::BaseRuntimeSimd:
6752 assert(!ChunkSize &&
6753 "schedule type does not support user-defined chunk sizes");
6754 [[fallthrough]];
6755 case OMPScheduleType::BaseGuidedSimd:
6756 case OMPScheduleType::BaseDynamicChunked:
6757 case OMPScheduleType::BaseGuidedChunked:
6758 case OMPScheduleType::BaseGuidedIterativeChunked:
6759 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6760 case OMPScheduleType::BaseStaticBalancedChunked:
6761 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6762 NeedsBarrier, ChunkSize);
6763
6764 default:
6765 llvm_unreachable("Unknown/unimplemented schedule kind");
6766 }
6767}
6768
6769/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6770/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6771/// the runtime. Always interpret integers as unsigned similarly to
6772/// CanonicalLoopInfo.
6773static FunctionCallee
6775 unsigned Bitwidth = Ty->getIntegerBitWidth();
6776 if (Bitwidth == 32)
6777 return OMPBuilder.getOrCreateRuntimeFunction(
6778 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6779 if (Bitwidth == 64)
6780 return OMPBuilder.getOrCreateRuntimeFunction(
6781 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6782 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6783}
6784
6785/// Returns an LLVM function to call for updating the next loop using OpenMP
6786/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6787/// the runtime. Always interpret integers as unsigned similarly to
6788/// CanonicalLoopInfo.
6789static FunctionCallee
6791 unsigned Bitwidth = Ty->getIntegerBitWidth();
6792 if (Bitwidth == 32)
6793 return OMPBuilder.getOrCreateRuntimeFunction(
6794 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6795 if (Bitwidth == 64)
6796 return OMPBuilder.getOrCreateRuntimeFunction(
6797 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6798 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6799}
6800
6801/// Returns an LLVM function to call for finalizing the dynamic loop using
6802/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6803/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6804static FunctionCallee
6806 unsigned Bitwidth = Ty->getIntegerBitWidth();
6807 if (Bitwidth == 32)
6808 return OMPBuilder.getOrCreateRuntimeFunction(
6809 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6810 if (Bitwidth == 64)
6811 return OMPBuilder.getOrCreateRuntimeFunction(
6812 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6813 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6814}
6815
6817OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6818 InsertPointTy AllocaIP,
6819 OMPScheduleType SchedType,
6820 bool NeedsBarrier, Value *Chunk) {
6821 assert(CLI->isValid() && "Requires a valid canonical loop");
6822 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6823 "Require dedicated allocate IP");
6825 "Require valid schedule type");
6826
6827 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6828 OMPScheduleType::ModifierOrdered;
6829
6830 // Set up the source location value for OpenMP runtime.
6831 Builder.SetCurrentDebugLocation(DL);
6832
6833 uint32_t SrcLocStrSize;
6834 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6835 Value *SrcLoc =
6836 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6837
6838 // Declare useful OpenMP runtime functions.
6839 Value *IV = CLI->getIndVar();
6840 Type *IVTy = IV->getType();
6841 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6842 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6843
6844 // Allocate space for computed loop bounds as expected by the "init" function.
6845 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6846 Type *I32Type = Type::getInt32Ty(M.getContext());
6847 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6848 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6849 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6850 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6851 CLI->setLastIter(PLastIter);
6852
6853 // At the end of the preheader, prepare for calling the "init" function by
6854 // storing the current loop bounds into the allocated space. A canonical loop
6855 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6856 // and produces an inclusive upper bound.
6857 BasicBlock *PreHeader = CLI->getPreheader();
6858 Builder.SetInsertPoint(PreHeader->getTerminator());
6859 Constant *One = ConstantInt::get(IVTy, 1);
6860 Builder.CreateStore(One, PLowerBound);
6861 Value *UpperBound = CLI->getTripCount();
6862 Builder.CreateStore(UpperBound, PUpperBound);
6863 Builder.CreateStore(One, PStride);
6864
6865 BasicBlock *Header = CLI->getHeader();
6866 BasicBlock *Exit = CLI->getExit();
6867 BasicBlock *Cond = CLI->getCond();
6868 BasicBlock *Latch = CLI->getLatch();
6869 InsertPointTy AfterIP = CLI->getAfterIP();
6870
6871 // The CLI will be "broken" in the code below, as the loop is no longer
6872 // a valid canonical loop.
6873
6874 if (!Chunk)
6875 Chunk = One;
6876
6877 Value *ThreadNum =
6878 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6879
6880 Constant *SchedulingType =
6881 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6882
6883 // Call the "init" function.
6884 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6885 /* LowerBound */ One, UpperBound,
6886 /* step */ One, Chunk});
6887
6888 // An outer loop around the existing one.
6889 BasicBlock *OuterCond = BasicBlock::Create(
6890 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6891 PreHeader->getParent());
6892 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6893 Builder.SetInsertPoint(OuterCond, OuterCond->getFirstInsertionPt());
6895 DynamicNext,
6896 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6897 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6898 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6899 Value *LowerBound =
6900 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6901 Builder.CreateCondBr(MoreWork, Header, Exit);
6902
6903 // Change PHI-node in loop header to use outer cond rather than preheader,
6904 // and set IV to the LowerBound.
6905 Instruction *Phi = &Header->front();
6906 auto *PI = cast<PHINode>(Phi);
6907 PI->setIncomingBlock(0, OuterCond);
6908 PI->setIncomingValue(0, LowerBound);
6909
6910 // Then set the pre-header to jump to the OuterCond
6911 Instruction *Term = PreHeader->getTerminator();
6912 auto *Br = cast<UncondBrInst>(Term);
6913 Br->setSuccessor(OuterCond);
6914
6915 // Modify the inner condition:
6916 // * Use the UpperBound returned from the DynamicNext call.
6917 // * jump to the loop outer loop when done with one of the inner loops.
6918 Builder.SetInsertPoint(Cond, Cond->getFirstInsertionPt());
6919 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
6920 Instruction *Comp = &*Builder.GetInsertPoint();
6921 auto *CI = cast<CmpInst>(Comp);
6922 CI->setOperand(1, UpperBound);
6923 // Redirect the inner exit to branch to outer condition.
6924 Instruction *Branch = &Cond->back();
6925 auto *BI = cast<CondBrInst>(Branch);
6926 assert(BI->getSuccessor(1) == Exit);
6927 BI->setSuccessor(1, OuterCond);
6928
6929 // Call the "fini" function if "ordered" is present in wsloop directive.
6930 if (Ordered) {
6931 Builder.SetInsertPoint(&Latch->back());
6932 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
6933 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
6934 }
6935
6936 // Add the barrier if requested.
6937 if (NeedsBarrier) {
6938 Builder.SetInsertPoint(&Exit->back());
6939 InsertPointOrErrorTy BarrierIP =
6941 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6942 /* CheckCancelFlag */ false);
6943 if (!BarrierIP)
6944 return BarrierIP.takeError();
6945 }
6946
6947 CLI->invalidate();
6948 return AfterIP;
6949}
6950
6951/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
6952/// after this \p OldTarget will be orphaned.
6954 BasicBlock *NewTarget, DebugLoc DL) {
6955 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
6956 redirectTo(Pred, NewTarget, DL);
6957}
6958
6960 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
6961 // We add a block to BBsToKeep iff we have proven it has an external use.
6963
6964 while (true) {
6965 bool Changed = false;
6966
6967 for (BasicBlock *BB : BBs) {
6968 if (BBsToKeep.contains(BB))
6969 continue;
6970
6971 for (Use &U : BB->uses()) {
6972 auto *UseInst = dyn_cast<Instruction>(U.getUser());
6973 if (!UseInst)
6974 continue;
6975 BasicBlock *UseBB = UseInst->getParent();
6976 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
6977 BBsToKeep.insert(BB);
6978 Changed = true;
6979 break;
6980 }
6981 }
6982 }
6983
6984 if (!Changed)
6985 break;
6986 }
6987
6989 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
6990 DeleteDeadBlocks(BBsToDelete);
6991}
6992
6993CanonicalLoopInfo *
6995 InsertPointTy ComputeIP) {
6996 assert(Loops.size() >= 1 && "At least one loop required");
6997 size_t NumLoops = Loops.size();
6998
6999 // Nothing to do if there is already just one loop.
7000 if (NumLoops == 1)
7001 return Loops.front();
7002
7003 CanonicalLoopInfo *Outermost = Loops.front();
7004 CanonicalLoopInfo *Innermost = Loops.back();
7005 BasicBlock *OrigPreheader = Outermost->getPreheader();
7006 BasicBlock *OrigAfter = Outermost->getAfter();
7007 Function *F = OrigPreheader->getParent();
7008
7009 // Loop control blocks that may become orphaned later.
7010 SmallVector<BasicBlock *, 12> OldControlBBs;
7011 OldControlBBs.reserve(6 * Loops.size());
7013 Loop->collectControlBlocks(OldControlBBs);
7014
7015 // Setup the IRBuilder for inserting the trip count computation.
7016 Builder.SetCurrentDebugLocation(DL);
7017 if (ComputeIP.isSet())
7018 Builder.restoreIP(ComputeIP);
7019 else
7020 Builder.restoreIP(Outermost->getPreheaderIP());
7021
7022 // Derive the collapsed' loop trip count.
7023 // TODO: Find common/largest indvar type.
7024 Value *CollapsedTripCount = nullptr;
7025 for (CanonicalLoopInfo *L : Loops) {
7026 assert(L->isValid() &&
7027 "All loops to collapse must be valid canonical loops");
7028 Value *OrigTripCount = L->getTripCount();
7029 if (!CollapsedTripCount) {
7030 CollapsedTripCount = OrigTripCount;
7031 continue;
7032 }
7033
7034 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7035 CollapsedTripCount =
7036 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7037 }
7038
7039 // Create the collapsed loop control flow.
7040 CanonicalLoopInfo *Result =
7041 createLoopSkeleton(DL, CollapsedTripCount, F,
7042 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7043 /*IsCollapsed=*/true);
7044
7045 // Build the collapsed loop body code.
7046 // Start with deriving the input loop induction variables from the collapsed
7047 // one, using a divmod scheme. To preserve the original loops' order, the
7048 // innermost loop use the least significant bits.
7049 Builder.restoreIP(Result->getBodyIP());
7050
7051 Value *Leftover = Result->getIndVar();
7052 SmallVector<Value *> NewIndVars;
7053 NewIndVars.resize(NumLoops);
7054 for (int i = NumLoops - 1; i >= 1; --i) {
7055 Value *OrigTripCount = Loops[i]->getTripCount();
7056
7057 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7058 NewIndVars[i] = NewIndVar;
7059
7060 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7061 }
7062 // Outermost loop gets all the remaining bits.
7063 NewIndVars[0] = Leftover;
7064
7065 // Construct the loop body control flow.
7066 // We progressively construct the branch structure following in direction of
7067 // the control flow, from the leading in-between code, the loop nest body, the
7068 // trailing in-between code, and rejoining the collapsed loop's latch.
7069 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7070 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7071 // its predecessors as sources.
7072 BasicBlock *ContinueBlock = Result->getBody();
7073 BasicBlock *ContinuePred = nullptr;
7074 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7075 BasicBlock *NextSrc) {
7076 if (ContinueBlock)
7077 redirectTo(ContinueBlock, Dest, DL);
7078 else
7079 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7080
7081 ContinueBlock = nullptr;
7082 ContinuePred = NextSrc;
7083 };
7084
7085 // The code before the nested loop of each level.
7086 // Because we are sinking it into the nest, it will be executed more often
7087 // that the original loop. More sophisticated schemes could keep track of what
7088 // the in-between code is and instantiate it only once per thread.
7089 for (size_t i = 0; i < NumLoops - 1; ++i)
7090 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7091
7092 // Connect the loop nest body.
7093 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7094
7095 // The code after the nested loop at each level.
7096 for (size_t i = NumLoops - 1; i > 0; --i)
7097 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7098
7099 // Connect the finished loop to the collapsed loop latch.
7100 ContinueWith(Result->getLatch(), nullptr);
7101
7102 // Replace the input loops with the new collapsed loop.
7103 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7104 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7105
7106 // Replace the input loop indvars with the derived ones.
7107 for (size_t i = 0; i < NumLoops; ++i)
7108 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7109
7110 // Remove unused parts of the input loops.
7111 removeUnusedBlocksFromParent(OldControlBBs);
7112
7113 for (CanonicalLoopInfo *L : Loops)
7114 L->invalidate();
7115
7116#ifndef NDEBUG
7117 Result->assertOK();
7118#endif
7119 return Result;
7120}
7121
7122std::vector<CanonicalLoopInfo *>
7124 ArrayRef<Value *> TileSizes) {
7125 assert(TileSizes.size() == Loops.size() &&
7126 "Must pass as many tile sizes as there are loops");
7127 int NumLoops = Loops.size();
7128 assert(NumLoops >= 1 && "At least one loop to tile required");
7129
7130 CanonicalLoopInfo *OutermostLoop = Loops.front();
7131 CanonicalLoopInfo *InnermostLoop = Loops.back();
7132 Function *F = OutermostLoop->getBody()->getParent();
7133 BasicBlock *InnerEnter = InnermostLoop->getBody();
7134 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7135
7136 // Loop control blocks that may become orphaned later.
7137 SmallVector<BasicBlock *, 12> OldControlBBs;
7138 OldControlBBs.reserve(6 * Loops.size());
7140 Loop->collectControlBlocks(OldControlBBs);
7141
7142 // Collect original trip counts and induction variable to be accessible by
7143 // index. Also, the structure of the original loops is not preserved during
7144 // the construction of the tiled loops, so do it before we scavenge the BBs of
7145 // any original CanonicalLoopInfo.
7146 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7147 for (CanonicalLoopInfo *L : Loops) {
7148 assert(L->isValid() && "All input loops must be valid canonical loops");
7149 OrigTripCounts.push_back(L->getTripCount());
7150 OrigIndVars.push_back(L->getIndVar());
7151 }
7152
7153 // Collect the code between loop headers. These may contain SSA definitions
7154 // that are used in the loop nest body. To be usable with in the innermost
7155 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7156 // these instructions may be executed more often than before the tiling.
7157 // TODO: It would be sufficient to only sink them into body of the
7158 // corresponding tile loop.
7160 for (int i = 0; i < NumLoops - 1; ++i) {
7161 CanonicalLoopInfo *Surrounding = Loops[i];
7162 CanonicalLoopInfo *Nested = Loops[i + 1];
7163
7164 BasicBlock *EnterBB = Surrounding->getBody();
7165 BasicBlock *ExitBB = Nested->getHeader();
7166 InbetweenCode.emplace_back(EnterBB, ExitBB);
7167 }
7168
7169 // Compute the trip counts of the floor loops.
7170 Builder.SetCurrentDebugLocation(DL);
7171 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7172 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7173 for (int i = 0; i < NumLoops; ++i) {
7174 Value *TileSize = TileSizes[i];
7175 Value *OrigTripCount = OrigTripCounts[i];
7176 Type *IVType = OrigTripCount->getType();
7177
7178 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7179 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7180
7181 // 0 if tripcount divides the tilesize, 1 otherwise.
7182 // 1 means we need an additional iteration for a partial tile.
7183 //
7184 // Unfortunately we cannot just use the roundup-formula
7185 // (tripcount + tilesize - 1)/tilesize
7186 // because the summation might overflow. We do not want introduce undefined
7187 // behavior when the untiled loop nest did not.
7188 Value *FloorTripOverflow =
7189 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7190
7191 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7192 Value *FloorTripCount =
7193 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7194 "omp_floor" + Twine(i) + ".tripcount", true);
7195
7196 // Remember some values for later use.
7197 FloorCompleteCount.push_back(FloorCompleteTripCount);
7198 FloorCount.push_back(FloorTripCount);
7199 FloorRems.push_back(FloorTripRem);
7200 }
7201
7202 // Generate the new loop nest, from the outermost to the innermost.
7203 std::vector<CanonicalLoopInfo *> Result;
7204 Result.reserve(NumLoops * 2);
7205
7206 // The basic block of the surrounding loop that enters the nest generated
7207 // loop.
7208 BasicBlock *Enter = OutermostLoop->getPreheader();
7209
7210 // The basic block of the surrounding loop where the inner code should
7211 // continue.
7212 BasicBlock *Continue = OutermostLoop->getAfter();
7213
7214 // Where the next loop basic block should be inserted.
7215 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7216
7217 auto EmbeddNewLoop =
7218 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7219 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7220 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7221 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7222 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7223 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7224
7225 // Setup the position where the next embedded loop connects to this loop.
7226 Enter = EmbeddedLoop->getBody();
7227 Continue = EmbeddedLoop->getLatch();
7228 OutroInsertBefore = EmbeddedLoop->getLatch();
7229 return EmbeddedLoop;
7230 };
7231
7232 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7233 const Twine &NameBase) {
7234 for (auto P : enumerate(TripCounts)) {
7235 CanonicalLoopInfo *EmbeddedLoop =
7236 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7237 Result.push_back(EmbeddedLoop);
7238 }
7239 };
7240
7241 EmbeddNewLoops(FloorCount, "floor");
7242
7243 // Within the innermost floor loop, emit the code that computes the tile
7244 // sizes.
7245 Builder.SetInsertPoint(Enter->getTerminator());
7246 SmallVector<Value *, 4> TileCounts;
7247 for (int i = 0; i < NumLoops; ++i) {
7248 CanonicalLoopInfo *FloorLoop = Result[i];
7249 Value *TileSize = TileSizes[i];
7250
7251 Value *FloorIsEpilogue =
7252 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7253 Value *TileTripCount =
7254 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7255
7256 TileCounts.push_back(TileTripCount);
7257 }
7258
7259 // Create the tile loops.
7260 EmbeddNewLoops(TileCounts, "tile");
7261
7262 // Insert the inbetween code into the body.
7263 BasicBlock *BodyEnter = Enter;
7264 BasicBlock *BodyEntered = nullptr;
7265 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7266 BasicBlock *EnterBB = P.first;
7267 BasicBlock *ExitBB = P.second;
7268
7269 if (BodyEnter)
7270 redirectTo(BodyEnter, EnterBB, DL);
7271 else
7272 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7273
7274 BodyEnter = nullptr;
7275 BodyEntered = ExitBB;
7276 }
7277
7278 // Append the original loop nest body into the generated loop nest body.
7279 if (BodyEnter)
7280 redirectTo(BodyEnter, InnerEnter, DL);
7281 else
7282 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7284
7285 // Replace the original induction variable with an induction variable computed
7286 // from the tile and floor induction variables.
7287 Builder.restoreIP(Result.back()->getBodyIP());
7288 for (int i = 0; i < NumLoops; ++i) {
7289 CanonicalLoopInfo *FloorLoop = Result[i];
7290 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7291 Value *OrigIndVar = OrigIndVars[i];
7292 Value *Size = TileSizes[i];
7293
7294 Value *Scale =
7295 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7296 Value *Shift =
7297 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7298 OrigIndVar->replaceAllUsesWith(Shift);
7299 }
7300
7301 // Remove unused parts of the original loops.
7302 removeUnusedBlocksFromParent(OldControlBBs);
7303
7304 for (CanonicalLoopInfo *L : Loops)
7305 L->invalidate();
7306
7307#ifndef NDEBUG
7308 for (CanonicalLoopInfo *GenL : Result)
7309 GenL->assertOK();
7310#endif
7311 return Result;
7312}
7313
7314/// Attach metadata \p Properties to the basic block described by \p BB. If the
7315/// basic block already has metadata, the basic block properties are appended.
7317 ArrayRef<Metadata *> Properties) {
7318 // Nothing to do if no property to attach.
7319 if (Properties.empty())
7320 return;
7321
7322 LLVMContext &Ctx = BB->getContext();
7323 SmallVector<Metadata *> NewProperties;
7324 NewProperties.push_back(nullptr);
7325
7326 // If the basic block already has metadata, prepend it to the new metadata.
7327 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7328 if (Existing)
7329 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7330
7331 append_range(NewProperties, Properties);
7332 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7333 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7334
7335 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7336}
7337
7338/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7339/// loop already has metadata, the loop properties are appended.
7341 ArrayRef<Metadata *> Properties) {
7342 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7343
7344 // Attach metadata to the loop's latch
7345 BasicBlock *Latch = Loop->getLatch();
7346 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7347 addBasicBlockMetadata(Latch, Properties);
7348}
7349
7350/// Attach llvm.access.group metadata to the memref instructions of \p Block
7352 LoopInfo &LI) {
7353 for (Instruction &I : *Block) {
7354 if (I.mayReadOrWriteMemory()) {
7355 // TODO: This instruction may already have access group from
7356 // other pragmas e.g. #pragma clang loop vectorize. Append
7357 // so that the existing metadata is not overwritten.
7358 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7359 }
7360 }
7361}
7362
7363CanonicalLoopInfo *
7365 CanonicalLoopInfo *firstLoop = Loops.front();
7366 CanonicalLoopInfo *lastLoop = Loops.back();
7367 Function *F = firstLoop->getPreheader()->getParent();
7368
7369 // Loop control blocks that will become orphaned later
7370 SmallVector<BasicBlock *> oldControlBBs;
7372 Loop->collectControlBlocks(oldControlBBs);
7373
7374 // Collect original trip counts
7375 SmallVector<Value *> origTripCounts;
7376 for (CanonicalLoopInfo *L : Loops) {
7377 assert(L->isValid() && "All input loops must be valid canonical loops");
7378 origTripCounts.push_back(L->getTripCount());
7379 }
7380
7381 Builder.SetCurrentDebugLocation(DL);
7382
7383 // Compute max trip count.
7384 // The fused loop will be from 0 to max(origTripCounts)
7385 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7386 F, firstLoop->getHeader());
7387 Builder.SetInsertPoint(TCBlock);
7388 Value *fusedTripCount = nullptr;
7389 for (CanonicalLoopInfo *L : Loops) {
7390 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7391 Value *origTripCount = L->getTripCount();
7392 if (!fusedTripCount) {
7393 fusedTripCount = origTripCount;
7394 continue;
7395 }
7396 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7397 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7398 ".omp.fuse.tc");
7399 }
7400
7401 // Generate new loop
7402 CanonicalLoopInfo *fused =
7403 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7404 lastLoop->getLatch(), "fused");
7405
7406 // Replace original loops with the fused loop
7407 // Preheader and After are not considered inside the CLI.
7408 // These are used to compute the individual TCs of the loops
7409 // so they have to be put before the resulting fused loop.
7410 // Moving them up for readability.
7411 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7412 Loops[i]->getPreheader()->moveBefore(TCBlock);
7413 Loops[i]->getAfter()->moveBefore(TCBlock);
7414 }
7415 lastLoop->getPreheader()->moveBefore(TCBlock);
7416
7417 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7418 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7419 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7420 }
7421 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7422 redirectTo(TCBlock, fused->getPreheader(), DL);
7423 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7424
7425 // Build the fused body
7426 // Create new Blocks with conditions that jump to the original loop bodies
7428 SmallVector<Value *> condValues;
7429 for (size_t i = 0; i < Loops.size(); ++i) {
7430 BasicBlock *condBlock = BasicBlock::Create(
7431 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7432 Builder.SetInsertPoint(condBlock);
7433 Value *condValue =
7434 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7435 condBBs.push_back(condBlock);
7436 condValues.push_back(condValue);
7437 }
7438 // Join the condition blocks with the bodies of the original loops
7439 redirectTo(fused->getBody(), condBBs[0], DL);
7440 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7441 Builder.SetInsertPoint(condBBs[i]);
7442 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7443 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7444 // Replace the IV with the fused IV
7445 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7446 }
7447 // Last body jumps to the created end body block
7448 Builder.SetInsertPoint(condBBs.back());
7449 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7450 fused->getLatch());
7451 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7452 // Replace the IV with the fused IV
7453 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7454
7455 // The loop latch must have only one predecessor. Currently it is branched to
7456 // from both the last condition block and the last loop body
7457 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7458 "omp.fused.pre_latch");
7459
7460 // Remove unused parts
7461 removeUnusedBlocksFromParent(oldControlBBs);
7462
7463 // Invalidate old CLIs
7464 for (CanonicalLoopInfo *L : Loops)
7465 L->invalidate();
7466
7467#ifndef NDEBUG
7468 fused->assertOK();
7469#endif
7470 return fused;
7471}
7472
7474 LLVMContext &Ctx = Builder.getContext();
7476 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7477 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7478}
7479
7481 LLVMContext &Ctx = Builder.getContext();
7483 Loop, {
7484 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7485 });
7486}
7487
7488void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7489 Value *IfCond, ValueToValueMapTy &VMap,
7490 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7491 const Twine &NamePrefix) {
7492 Function *F = CanonicalLoop->getFunction();
7493
7494 // We can't do
7495 // if (cond) {
7496 // simd_loop;
7497 // } else {
7498 // non_simd_loop;
7499 // }
7500 // because then the CanonicalLoopInfo would only point to one of the loops:
7501 // leading to other constructs operating on the same loop to malfunction.
7502 // Instead generate
7503 // while (...) {
7504 // if (cond) {
7505 // simd_body;
7506 // } else {
7507 // not_simd_body;
7508 // }
7509 // }
7510 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7511 // body at -O3
7512
7513 // Define where if branch should be inserted
7514 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7515
7516 // Create additional blocks for the if statement
7517 BasicBlock *Cond = SplitBeforeIt->getParent();
7518 llvm::LLVMContext &C = Cond->getContext();
7520 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7522 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7523
7524 // Create if condition branch.
7525 Builder.SetInsertPoint(SplitBeforeIt);
7526 Instruction *BrInstr =
7527 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7528 InsertPointTy IP{BrInstr->getParent(), ++BrInstr->getIterator()};
7529 // Then block contains branch to omp loop body which needs to be vectorized
7530 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7531 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7532
7533 Builder.SetInsertPoint(ElseBlock);
7534
7535 // Clone loop for the else branch
7537
7538 SmallVector<BasicBlock *, 8> ExistingBlocks;
7539 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7540 ExistingBlocks.push_back(ThenBlock);
7541 ExistingBlocks.append(L->block_begin(), L->block_end());
7542 // Cond is the block that has the if clause condition
7543 // LoopCond is omp_loop.cond
7544 // LoopHeader is omp_loop.header
7545 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7546 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7547 assert(LoopCond && LoopHeader && "Invalid loop structure");
7548 for (BasicBlock *Block : ExistingBlocks) {
7549 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7550 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7551 continue;
7552 }
7553 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7554
7555 // fix name not to be omp.if.then
7556 if (Block == ThenBlock)
7557 NewBB->setName(NamePrefix + ".if.else");
7558
7559 NewBB->moveBefore(CanonicalLoop->getExit());
7560 VMap[Block] = NewBB;
7561 NewBlocks.push_back(NewBB);
7562 }
7563 remapInstructionsInBlocks(NewBlocks, VMap);
7564 Builder.CreateBr(NewBlocks.front());
7565
7566 // The loop latch must have only one predecessor. Currently it is branched to
7567 // from both the 'then' and 'else' branches.
7568 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7569 NamePrefix + ".pre_latch");
7570
7571 // Ensure that the then block is added to the loop so we add the attributes in
7572 // the next step
7573 L->addBasicBlockToLoop(ThenBlock, LI);
7574}
7575
7576unsigned
7578 const StringMap<bool> &Features) {
7579 if (TargetTriple.isX86()) {
7580 if (Features.lookup("avx512f"))
7581 return 512;
7582 else if (Features.lookup("avx"))
7583 return 256;
7584 return 128;
7585 }
7586 if (TargetTriple.isPPC())
7587 return 128;
7588 if (TargetTriple.isWasm())
7589 return 128;
7590 return 0;
7591}
7592
7594 MapVector<Value *, Value *> AlignedVars,
7595 Value *IfCond, OrderKind Order,
7596 ConstantInt *Simdlen, ConstantInt *Safelen) {
7597 LLVMContext &Ctx = Builder.getContext();
7598
7599 Function *F = CanonicalLoop->getFunction();
7600
7601 // Blocks must have terminators.
7602 // FIXME: Don't run analyses on incomplete/invalid IR.
7604 for (BasicBlock &BB : *F)
7605 if (!BB.hasTerminator())
7606 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7607
7608 // TODO: We should not rely on pass manager. Currently we use pass manager
7609 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7610 // object. We should have a method which returns all blocks between
7611 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7613 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7614 FAM.registerPass([]() { return LoopAnalysis(); });
7615 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7616
7617 LoopAnalysis LIA;
7618 LoopInfo &&LI = LIA.run(*F, FAM);
7619
7620 for (Instruction *I : UIs)
7621 I->eraseFromParent();
7622
7623 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7624 if (AlignedVars.size()) {
7625 InsertPointTy IP = Builder.saveIP();
7626 for (auto &AlignedItem : AlignedVars) {
7627 Value *AlignedPtr = AlignedItem.first;
7628 Value *Alignment = AlignedItem.second;
7629 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7630 Builder.SetInsertPoint(loadInst->getNextNode());
7631 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7632 Alignment);
7633 }
7634 Builder.restoreIP(IP);
7635 }
7636
7637 if (IfCond) {
7638 ValueToValueMapTy VMap;
7639 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7640 }
7641
7643
7644 // Get the basic blocks from the loop in which memref instructions
7645 // can be found.
7646 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7647 // preferably without running any passes.
7648 for (BasicBlock *Block : L->getBlocks()) {
7649 if (Block == CanonicalLoop->getCond() ||
7650 Block == CanonicalLoop->getHeader())
7651 continue;
7652 Reachable.insert(Block);
7653 }
7654
7655 SmallVector<Metadata *> LoopMDList;
7656
7657 // In presence of finite 'safelen', it may be unsafe to mark all
7658 // the memory instructions parallel, because loop-carried
7659 // dependences of 'safelen' iterations are possible.
7660 // If clause order(concurrent) is specified then the memory instructions
7661 // are marked parallel even if 'safelen' is finite.
7662 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7663 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7664
7665 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7666 // versions so we can't add the loop attributes in that case.
7667 if (IfCond) {
7668 // we can still add llvm.loop.parallel_access
7669 addLoopMetadata(CanonicalLoop, LoopMDList);
7670 return;
7671 }
7672
7673 // Use the above access group metadata to create loop level
7674 // metadata, which should be distinct for each loop.
7675 LoopMDList.push_back(
7676 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7677
7678 if (Simdlen || Safelen) {
7679 // If both simdlen and safelen clauses are specified, the value of the
7680 // simdlen parameter must be less than or equal to the value of the safelen
7681 // parameter. Therefore, use safelen only in the absence of simdlen.
7682 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7683 LoopMDList.push_back(
7684 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7685 ConstantAsMetadata::get(VectorizeWidth)}));
7686 }
7687
7688 addLoopMetadata(CanonicalLoop, LoopMDList);
7689}
7690
7691/// Create the TargetMachine object to query the backend for optimization
7692/// preferences.
7693///
7694/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7695/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7696/// needed for the LLVM pass pipline. We use some default options to avoid
7697/// having to pass too many settings from the frontend that probably do not
7698/// matter.
7699///
7700/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7701/// method. If we are going to use TargetMachine for more purposes, especially
7702/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7703/// might become be worth requiring front-ends to pass on their TargetMachine,
7704/// or at least cache it between methods. Note that while fontends such as Clang
7705/// have just a single main TargetMachine per translation unit, "target-cpu" and
7706/// "target-features" that determine the TargetMachine are per-function and can
7707/// be overrided using __attribute__((target("OPTIONS"))).
7708static std::unique_ptr<TargetMachine>
7710 Module *M = F->getParent();
7711
7712 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7713 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7714 const llvm::Triple &Triple = M->getTargetTriple();
7715
7716 std::string Error;
7718 if (!TheTarget)
7719 return {};
7720
7722 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7723 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7724 /*CodeModel=*/std::nullopt, OptLevel));
7725}
7726
7727/// Heuristically determine the best-performant unroll factor for \p CLI. This
7728/// depends on the target processor. We are re-using the same heuristics as the
7729/// LoopUnrollPass.
7731 Function *F = CLI->getFunction();
7732
7733 // Assume the user requests the most aggressive unrolling, even if the rest of
7734 // the code is optimized using a lower setting.
7736 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7737
7738 // Blocks must have terminators.
7739 // FIXME: Don't run analyses on incomplete/invalid IR.
7741 for (BasicBlock &BB : *F)
7742 if (!BB.hasTerminator())
7743 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7744
7746 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7747 FAM.registerPass([]() { return AssumptionAnalysis(); });
7748 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7749 FAM.registerPass([]() { return LoopAnalysis(); });
7750 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7751 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7752 TargetIRAnalysis TIRA;
7753 if (TM)
7754 TIRA = TargetIRAnalysis(
7755 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7756 FAM.registerPass([&]() { return TIRA; });
7757
7758 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7760 ScalarEvolution &&SE = SEA.run(*F, FAM);
7762 DominatorTree &&DT = DTA.run(*F, FAM);
7763 LoopAnalysis LIA;
7764 LoopInfo &&LI = LIA.run(*F, FAM);
7766 AssumptionCache &&AC = ACT.run(*F, FAM);
7768
7769 for (Instruction *I : UIs)
7770 I->eraseFromParent();
7771
7772 Loop *L = LI.getLoopFor(CLI->getHeader());
7773 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7774
7776 L, SE, TTI,
7777 /*BlockFrequencyInfo=*/nullptr,
7778 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7779 /*UserThreshold=*/std::nullopt,
7780 /*UserAllowPartial=*/true,
7781 /*UserAllowRuntime=*/true,
7782 /*UserUpperBound=*/std::nullopt,
7783 /*UserFullUnrollMaxCount=*/std::nullopt);
7784
7785 UP.Force = true;
7786
7787 // Account for additional optimizations taking place before the LoopUnrollPass
7788 // would unroll the loop.
7791
7792 // Use normal unroll factors even if the rest of the code is optimized for
7793 // size.
7796
7797 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7798 << " Threshold=" << UP.Threshold << "\n"
7799 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7800 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7801 << " PartialOptSizeThreshold="
7802 << UP.PartialOptSizeThreshold << "\n");
7803
7804 // Disable peeling.
7807 /*UserAllowPeeling=*/false,
7808 /*UserAllowProfileBasedPeeling=*/false,
7809 /*UnrollingSpecficValues=*/false);
7810
7812 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7813
7814 // Assume that reads and writes to stack variables can be eliminated by
7815 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7816 // size.
7817 for (BasicBlock *BB : L->blocks()) {
7818 for (Instruction &I : *BB) {
7819 Value *Ptr;
7820 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7821 Ptr = Load->getPointerOperand();
7822 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7823 Ptr = Store->getPointerOperand();
7824 } else
7825 continue;
7826
7827 Ptr = Ptr->stripPointerCasts();
7828
7829 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7830 if (Alloca->getParent() == &F->getEntryBlock())
7831 EphValues.insert(&I);
7832 }
7833 }
7834 }
7835
7836 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7837
7838 // Loop is not unrollable if the loop contains certain instructions.
7839 if (!UCE.canUnroll()) {
7840 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7841 return 1;
7842 }
7843
7844 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7845 << "\n");
7846
7847 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7848 // be able to use it.
7849 int TripCount = 0;
7850 int MaxTripCount = 0;
7851 bool MaxOrZero = false;
7852 unsigned TripMultiple = 0;
7853
7854 unsigned Factor =
7855 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7856 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7857 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7858
7859 // This function returns 1 to signal to not unroll a loop.
7860 if (Factor == 0)
7861 return 1;
7862 return Factor;
7863}
7864
7866 int32_t Factor,
7867 CanonicalLoopInfo **UnrolledCLI) {
7868 assert(Factor >= 0 && "Unroll factor must not be negative");
7869
7870 Function *F = Loop->getFunction();
7871 LLVMContext &Ctx = F->getContext();
7872
7873 // If the unrolled loop is not used for another loop-associated directive, it
7874 // is sufficient to add metadata for the LoopUnrollPass.
7875 if (!UnrolledCLI) {
7876 SmallVector<Metadata *, 2> LoopMetadata;
7877 LoopMetadata.push_back(
7878 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7879
7880 if (Factor >= 1) {
7882 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7883 LoopMetadata.push_back(MDNode::get(
7884 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7885 }
7886
7887 addLoopMetadata(Loop, LoopMetadata);
7888 return;
7889 }
7890
7891 // Heuristically determine the unroll factor.
7892 if (Factor == 0)
7894
7895 // No change required with unroll factor 1.
7896 if (Factor == 1) {
7897 *UnrolledCLI = Loop;
7898 return;
7899 }
7900
7901 assert(Factor >= 2 &&
7902 "unrolling only makes sense with a factor of 2 or larger");
7903
7904 Type *IndVarTy = Loop->getIndVarType();
7905
7906 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7907 // unroll the inner loop.
7908 Value *FactorVal =
7909 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7910 /*isSigned=*/false));
7911 std::vector<CanonicalLoopInfo *> LoopNest =
7912 tileLoops(DL, {Loop}, {FactorVal});
7913 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7914 *UnrolledCLI = LoopNest[0];
7915 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7916
7917 // LoopUnrollPass can only fully unroll loops with constant trip count.
7918 // Unroll by the unroll factor with a fallback epilog for the remainder
7919 // iterations if necessary.
7921 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7923 InnerLoop,
7924 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7926 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
7927
7928#ifndef NDEBUG
7929 (*UnrolledCLI)->assertOK();
7930#endif
7931}
7932
7935 llvm::Value *BufSize, llvm::Value *CpyBuf,
7936 llvm::Value *CpyFn, llvm::Value *DidIt) {
7937 if (!updateToLocation(Loc))
7938 return Loc.IP;
7939
7940 uint32_t SrcLocStrSize;
7941 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7942 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7943 Value *ThreadId = getOrCreateThreadID(Ident);
7944
7945 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
7946
7947 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
7948
7949 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
7950 createRuntimeFunctionCall(Fn, Args);
7951
7952 return Builder.saveIP();
7953}
7954
7956 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7957 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
7959
7960 if (!updateToLocation(Loc))
7961 return Loc.IP;
7962
7963 // If needed allocate and initialize `DidIt` with 0.
7964 // DidIt: flag variable: 1=single thread; 0=not single thread.
7965 llvm::Value *DidIt = nullptr;
7966 if (!CPVars.empty()) {
7967 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
7968 Builder.CreateStore(Builder.getInt32(0), DidIt);
7969 }
7970
7971 Directive OMPD = Directive::OMPD_single;
7972 uint32_t SrcLocStrSize;
7973 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7974 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7975 Value *ThreadId = getOrCreateThreadID(Ident);
7976 Value *Args[] = {Ident, ThreadId};
7977
7978 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
7979 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
7980
7981 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
7982 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
7983
7984 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
7985 if (Error Err = FiniCB(IP))
7986 return Err;
7987
7988 // The thread that executes the single region must set `DidIt` to 1.
7989 // This is used by __kmpc_copyprivate, to know if the caller is the
7990 // single thread or not.
7991 if (DidIt)
7992 Builder.CreateStore(Builder.getInt32(1), DidIt);
7993
7994 return Error::success();
7995 };
7996
7997 // generates the following:
7998 // if (__kmpc_single()) {
7999 // .... single region ...
8000 // __kmpc_end_single
8001 // }
8002 // __kmpc_copyprivate
8003 // __kmpc_barrier
8004
8005 InsertPointOrErrorTy AfterIP =
8006 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8007 /*Conditional*/ true,
8008 /*hasFinalize*/ true);
8009 if (!AfterIP)
8010 return AfterIP.takeError();
8011
8012 if (DidIt) {
8013 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8014 // NOTE BufSize is currently unused, so just pass 0.
8016 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8017 CPFuncs[I], DidIt);
8018 // NOTE __kmpc_copyprivate already inserts a barrier
8019 } else if (!IsNowait) {
8020 InsertPointOrErrorTy AfterIP =
8022 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8023 /* CheckCancelFlag */ false);
8024 if (!AfterIP)
8025 return AfterIP.takeError();
8026 }
8027 return Builder.saveIP();
8028}
8029
8032 BodyGenCallbackTy BodyGenCB,
8033 FinalizeCallbackTy FiniCB, bool IsNowait) {
8034
8035 if (!updateToLocation(Loc))
8036 return Loc.IP;
8037
8038 // All threads execute the scope body — no conditional entry.
8039 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8040 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8041 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8042 /*IsCancellable=*/false);
8043 if (!AfterIP)
8044 return AfterIP.takeError();
8045
8046 Builder.restoreIP(*AfterIP);
8047 if (!IsNowait) {
8048 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8049 omp::Directive::OMPD_unknown,
8050 /*ForceSimpleCall=*/false,
8051 /*CheckCancelFlag=*/false);
8052 if (!AfterIP)
8053 return AfterIP.takeError();
8054 }
8055 return Builder.saveIP();
8056}
8057
8059 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8060 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8061
8062 if (!updateToLocation(Loc))
8063 return Loc.IP;
8064
8065 Directive OMPD = Directive::OMPD_critical;
8066 uint32_t SrcLocStrSize;
8067 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8068 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8069 Value *ThreadId = getOrCreateThreadID(Ident);
8070 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8071 Value *Args[] = {Ident, ThreadId, LockVar};
8072
8073 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8074 Function *RTFn = nullptr;
8075 if (HintInst) {
8076 // Add Hint to entry Args and create call
8077 EnterArgs.push_back(HintInst);
8078 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8079 } else {
8080 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8081 }
8082 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8083
8084 Function *ExitRTLFn =
8085 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8086 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8087
8088 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8089 /*Conditional*/ false, /*hasFinalize*/ true);
8090}
8091
8094 InsertPointTy AllocaIP, unsigned NumLoops,
8095 ArrayRef<llvm::Value *> StoreValues,
8096 const Twine &Name, bool IsDependSource) {
8097 assert(
8098 llvm::all_of(StoreValues,
8099 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8100 "OpenMP runtime requires depend vec with i64 type");
8101
8102 if (!updateToLocation(Loc))
8103 return Loc.IP;
8104
8105 // Allocate space for vector and generate alloc instruction.
8106 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8107 Builder.restoreIP(AllocaIP);
8108 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8109 ArgsBase->setAlignment(Align(8));
8111
8112 // Store the index value with offset in depend vector.
8113 for (unsigned I = 0; I < NumLoops; ++I) {
8114 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8115 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8116 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8117 STInst->setAlignment(Align(8));
8118 }
8119
8120 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8121 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8122
8123 uint32_t SrcLocStrSize;
8124 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8125 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8126 Value *ThreadId = getOrCreateThreadID(Ident);
8127 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8128
8129 Function *RTLFn = nullptr;
8130 if (IsDependSource)
8131 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8132 else
8133 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8134 createRuntimeFunctionCall(RTLFn, Args);
8135
8136 return Builder.saveIP();
8137}
8138
8140 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8141 FinalizeCallbackTy FiniCB, bool IsThreads) {
8142 if (!updateToLocation(Loc))
8143 return Loc.IP;
8144
8145 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8146 Instruction *EntryCall = nullptr;
8147 Instruction *ExitCall = nullptr;
8148
8149 if (IsThreads) {
8150 uint32_t SrcLocStrSize;
8151 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8152 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8153 Value *ThreadId = getOrCreateThreadID(Ident);
8154 Value *Args[] = {Ident, ThreadId};
8155
8156 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8157 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8158
8159 Function *ExitRTLFn =
8160 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8161 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8162 }
8163
8164 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8165 /*Conditional*/ false, /*hasFinalize*/ true);
8166}
8167
8168OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8169 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8170 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8171 bool HasFinalize, bool IsCancellable) {
8172
8173 if (HasFinalize)
8174 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8175
8176 // Create inlined region's entry and body blocks, in preparation
8177 // for conditional creation
8178 BasicBlock *EntryBB = Builder.GetInsertBlock();
8179 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8181 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8182 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8183 BasicBlock *FiniBB =
8184 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8185
8186 Builder.SetInsertPoint(EntryBB->getTerminator());
8187 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8188
8189 // generate body
8190 if (Error Err =
8191 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8192 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8193 return Err;
8194
8195 // emit exit call and do any needed finalization.
8196 auto FinIP = InsertPointTy(FiniBB, FiniBB->getFirstInsertionPt());
8197 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8198 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8199 "Unexpected control flow graph state!!");
8200 InsertPointOrErrorTy AfterIP =
8201 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8202 if (!AfterIP)
8203 return AfterIP.takeError();
8204
8205 // If we are skipping the region of a non conditional, remove the exit
8206 // block, and clear the builder's insertion point.
8207 assert(SplitPos->getParent() == ExitBB &&
8208 "Unexpected Insertion point location!");
8209 auto merged = MergeBlockIntoPredecessor(ExitBB);
8210 BasicBlock *ExitPredBB = SplitPos->getParent();
8211 auto InsertBB = merged ? ExitPredBB : ExitBB;
8213 SplitPos->eraseFromParent();
8214 Builder.SetInsertPoint(InsertBB);
8215
8216 return Builder.saveIP();
8217}
8218
8219OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8220 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8221 // if nothing to do, Return current insertion point.
8222 if (!Conditional || !EntryCall)
8223 return Builder.saveIP();
8224
8225 BasicBlock *EntryBB = Builder.GetInsertBlock();
8226 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8227 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8228 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8229
8230 // Emit thenBB and set the Builder's insertion point there for
8231 // body generation next. Place the block after the current block.
8232 Function *CurFn = EntryBB->getParent();
8233 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8234
8235 // Move Entry branch to end of ThenBB, and replace with conditional
8236 // branch (If-stmt)
8237 Instruction *EntryBBTI = EntryBB->getTerminator();
8238 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8239 EntryBBTI->removeFromParent();
8240 Builder.SetInsertPoint(UI);
8241 Builder.Insert(EntryBBTI);
8242 UI->eraseFromParent();
8243 Builder.SetInsertPoint(ThenBB->getTerminator());
8244
8245 // return an insertion point to ExitBB.
8246 return IRBuilder<>::InsertPoint(ExitBB, ExitBB->getFirstInsertionPt());
8247}
8248
8249OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8250 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8251 bool HasFinalize) {
8252
8253 Builder.restoreIP(FinIP);
8254
8255 // If there is finalization to do, emit it before the exit call
8256 if (HasFinalize) {
8257 assert(!FinalizationStack.empty() &&
8258 "Unexpected finalization stack state!");
8259
8260 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8261 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8262
8263 if (Error Err = Fi.mergeFiniBB(Builder, FinIP.getBlock()))
8264 return std::move(Err);
8265
8266 // Exit condition: insertion point is before the terminator of the new Fini
8267 // block
8268 Builder.SetInsertPoint(FinIP.getBlock()->getTerminator());
8269 }
8270
8271 if (!ExitCall)
8272 return Builder.saveIP();
8273
8274 // place the Exitcall as last instruction before Finalization block terminator
8275 ExitCall->removeFromParent();
8276 Builder.Insert(ExitCall);
8277
8278 return IRBuilder<>::InsertPoint(ExitCall->getParent(),
8279 ExitCall->getIterator());
8280}
8281
8283 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8284 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8285 if (!IP.isSet())
8286 return IP;
8287
8289
8290 // creates the following CFG structure
8291 // OMP_Entry : (MasterAddr != PrivateAddr)?
8292 // F T
8293 // | \
8294 // | copin.not.master
8295 // | /
8296 // v /
8297 // copyin.not.master.end
8298 // |
8299 // v
8300 // OMP.Entry.Next
8301
8302 BasicBlock *OMP_Entry = IP.getBlock();
8303 Function *CurFn = OMP_Entry->getParent();
8304 BasicBlock *CopyBegin =
8305 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8306 BasicBlock *CopyEnd = nullptr;
8307
8308 // If entry block is terminated, split to preserve the branch to following
8309 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8311 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8312 "copyin.not.master.end");
8313 OMP_Entry->getTerminator()->eraseFromParent();
8314 } else {
8315 CopyEnd =
8316 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8317 }
8318
8319 Builder.SetInsertPoint(OMP_Entry);
8320 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8321 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8322 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8323 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8324
8325 Builder.SetInsertPoint(CopyBegin);
8326 if (BranchtoEnd)
8327 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8328
8329 return Builder.saveIP();
8330}
8331
8333 Value *Size, Value *Allocator,
8334 std::string Name) {
8336 if (!updateToLocation(Loc))
8337 return nullptr;
8338
8339 uint32_t SrcLocStrSize;
8340 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8341 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8342 Value *ThreadId = getOrCreateThreadID(Ident);
8343 Value *Args[] = {ThreadId, Size, Allocator};
8344
8345 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8346
8347 return createRuntimeFunctionCall(Fn, Args, Name);
8348}
8349
8351 Value *Align, Value *Size,
8352 Value *Allocator,
8353 std::string Name) {
8355 if (!updateToLocation(Loc))
8356 return nullptr;
8357
8358 uint32_t SrcLocStrSize;
8359 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8360 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8361 Value *ThreadId = getOrCreateThreadID(Ident);
8362 Value *Args[] = {ThreadId, Align, Size, Allocator};
8363
8364 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8365
8366 return Builder.CreateCall(Fn, Args, Name);
8367}
8368
8370 Value *Addr, Value *Allocator,
8371 std::string Name) {
8373 if (!updateToLocation(Loc))
8374 return nullptr;
8375
8376 uint32_t SrcLocStrSize;
8377 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8378 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8379 Value *ThreadId = getOrCreateThreadID(Ident);
8380 Value *Args[] = {ThreadId, Addr, Allocator};
8381 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8382 return createRuntimeFunctionCall(Fn, Args, Name);
8383}
8384
8386 Value *Size,
8387 const Twine &Name) {
8390
8391 Value *Args[] = {Size};
8392 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8393 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8395 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8396 return Call;
8397}
8398
8400 Type *VarType,
8401 const Twine &Name) {
8402 return createOMPAllocShared(
8403 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8404}
8405
8407 Value *Addr, Value *Size,
8408 const Twine &Name) {
8411
8412 Value *Args[] = {Addr, Size};
8413 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8414 return Builder.CreateCall(Fn, Args, Name);
8415}
8416
8418 Value *Addr, Type *VarType,
8419 const Twine &Name) {
8420 return createOMPFreeShared(
8421 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8422 Name);
8423}
8424
8426 const LocationDescription &Loc, Value *InteropVar,
8427 omp::OMPInteropType InteropType, Value *Device, Value *NumDependences,
8428 Value *DependenceAddress, bool HaveNowaitClause) {
8431
8432 uint32_t SrcLocStrSize;
8433 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8434 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8435 Value *ThreadId = getOrCreateThreadID(Ident);
8436 if (Device == nullptr)
8438 else if (Device->getType() != Int32)
8439 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8440 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8441 if (NumDependences == nullptr) {
8442 NumDependences = ConstantInt::get(Int32, 0);
8443 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8444 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8445 }
8446 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8447 Value *Args[] = {
8448 Ident, ThreadId, InteropVar, InteropTypeVal,
8449 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8450
8451 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8452
8453 return createRuntimeFunctionCall(Fn, Args);
8454}
8455
8457 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8458 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8461
8462 uint32_t SrcLocStrSize;
8463 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8464 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8465 Value *ThreadId = getOrCreateThreadID(Ident);
8466 if (Device == nullptr)
8468 else if (Device->getType() != Int32)
8469 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8470 if (NumDependences == nullptr) {
8471 NumDependences = ConstantInt::get(Int32, 0);
8472 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8473 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8474 }
8475 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8476 Value *Args[] = {
8477 Ident, ThreadId, InteropVar, Device,
8478 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8479
8480 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8481
8482 return createRuntimeFunctionCall(Fn, Args);
8483}
8484
8486 Value *InteropVar, Value *Device,
8487 Value *NumDependences,
8488 Value *DependenceAddress,
8489 bool HaveNowaitClause) {
8492 uint32_t SrcLocStrSize;
8493 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8494 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8495 Value *ThreadId = getOrCreateThreadID(Ident);
8496 if (Device == nullptr)
8498 else if (Device->getType() != Int32)
8499 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8500 if (NumDependences == nullptr) {
8501 NumDependences = ConstantInt::get(Int32, 0);
8502 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8503 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8504 }
8505 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8506 Value *Args[] = {
8507 Ident, ThreadId, InteropVar, Device,
8508 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8509
8510 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8511
8512 return createRuntimeFunctionCall(Fn, Args);
8513}
8514
8517 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8520
8521 uint32_t SrcLocStrSize;
8522 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8523 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8524 Value *ThreadId = getOrCreateThreadID(Ident);
8525 Constant *ThreadPrivateCache =
8526 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8527 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8528
8529 Function *Fn =
8530 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8531
8532 return createRuntimeFunctionCall(Fn, Args);
8533}
8534
8536 const LocationDescription &Loc,
8538 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8539 "expected num_threads and num_teams to be specified");
8540
8541 if (!updateToLocation(Loc))
8542 return Loc.IP;
8543
8544 uint32_t SrcLocStrSize;
8545 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8546 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8547 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8548 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8549 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8550 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8551 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8552 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8553
8554 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8555 Function *Kernel = DebugKernelWrapper;
8556
8557 // We need to strip the debug prefix to get the correct kernel name.
8558 StringRef KernelName = Kernel->getName();
8559 const std::string DebugPrefix = "_debug__";
8560 if (KernelName.ends_with(DebugPrefix)) {
8561 KernelName = KernelName.drop_back(DebugPrefix.length());
8562 Kernel = M.getFunction(KernelName);
8563 assert(Kernel && "Expected the real kernel to exist");
8564 }
8565
8566 // Manifest the launch configuration in the metadata matching the kernel
8567 // environment.
8568 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8569 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8570 Attrs.MaxTeams.front());
8571
8572 // If MaxThreads is not set and needs adjustment, select the maximum between
8573 // the default workgroup size and the MinThreads value.
8574 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8575 if (MaxThreadsVal < 0 && UseDefaultMaxThreads) {
8576 if (hasGridValue(T)) {
8577 MaxThreadsVal =
8578 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8579 Attrs.MinThreads.front());
8580 } else {
8581 MaxThreadsVal = Attrs.MinThreads.front();
8582 }
8583 }
8584
8585 if (MaxThreadsVal > 0)
8586 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8587 MaxThreadsVal);
8588
8589 Constant *MinThreads =
8590 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8591 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8592 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8593 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8594 Constant *ReductionDataSize =
8595 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8596
8598 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8599 const DataLayout &DL = Fn->getDataLayout();
8600
8601 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8602 Constant *DynamicEnvironmentInitializer =
8603 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8604 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8605 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8606 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8607 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8608 DL.getDefaultGlobalsAddressSpace());
8609 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8610
8611 Constant *DynamicEnvironment =
8612 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8613 ? DynamicEnvironmentGV
8614 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8615 DynamicEnvironmentPtr);
8616
8617 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8618 ConfigurationEnvironment, {
8619 UseGenericStateMachineVal,
8620 MayUseNestedParallelismVal,
8621 IsSPMDVal,
8622 MinThreads,
8623 MaxThreads,
8624 MinTeams,
8625 MaxTeams,
8626 ReductionDataSize,
8627 });
8628 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8629 KernelEnvironment, {
8630 ConfigurationEnvironmentInitializer,
8631 Ident,
8632 DynamicEnvironment,
8633 });
8634 std::string KernelEnvironmentName =
8635 (KernelName + "_kernel_environment").str();
8636 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8637 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8638 KernelEnvironmentInitializer, KernelEnvironmentName,
8639 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8640 DL.getDefaultGlobalsAddressSpace());
8641 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8642
8643 Constant *KernelEnvironment =
8644 KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8645 ? KernelEnvironmentGV
8646 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8647 KernelEnvironmentPtr);
8648 Value *KernelLaunchEnvironment =
8649 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8650 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8651 KernelLaunchEnvironment =
8652 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8653 ? KernelLaunchEnvironment
8654 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8655 KernelLaunchEnvParamTy);
8656 CallInst *ThreadKind = createRuntimeFunctionCall(
8657 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8658
8659 Value *ExecUserCode = Builder.CreateICmpEQ(
8660 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8661 "exec_user_code");
8662
8663 // ThreadKind = __kmpc_target_init(...)
8664 // if (ThreadKind == -1)
8665 // user_code
8666 // else
8667 // return;
8668
8669 auto *UI = Builder.CreateUnreachable();
8670 BasicBlock *CheckBB = UI->getParent();
8671 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8672
8673 BasicBlock *WorkerExitBB = BasicBlock::Create(
8674 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8675 Builder.SetInsertPoint(WorkerExitBB);
8676 Builder.CreateRetVoid();
8677
8678 auto *CheckBBTI = CheckBB->getTerminator();
8679 Builder.SetInsertPoint(CheckBBTI);
8680 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8681
8682 CheckBBTI->eraseFromParent();
8683 UI->eraseFromParent();
8684
8685 // Continue in the "user_code" block, see diagram above and in
8686 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8687 return InsertPointTy(UserCodeEntryBB, UserCodeEntryBB->getFirstInsertionPt());
8688}
8689
8691 int32_t TeamsReductionDataSize) {
8692 if (!updateToLocation(Loc))
8693 return;
8694
8696 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8697
8699
8700 if (!TeamsReductionDataSize)
8701 return;
8702
8703 Function *Kernel = Builder.GetInsertBlock()->getParent();
8704 // We need to strip the debug prefix to get the correct kernel name.
8705 StringRef KernelName = Kernel->getName();
8706 const std::string DebugPrefix = "_debug__";
8707 if (KernelName.ends_with(DebugPrefix))
8708 KernelName = KernelName.drop_back(DebugPrefix.length());
8709 auto *KernelEnvironmentGV =
8710 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8711 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8712 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8713 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8714 KernelEnvironmentInitializer,
8715 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8716 KernelEnvironmentGV->setInitializer(NewInitializer);
8717}
8718
8719static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8720 bool Min) {
8721 if (Kernel.hasFnAttribute(Name)) {
8722 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8723 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8724 }
8725 Kernel.addFnAttr(Name, llvm::utostr(Value));
8726}
8727
8728std::pair<int32_t, int32_t>
8730 int32_t ThreadLimit =
8731 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8732
8733 if (T.isAMDGPU()) {
8734 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8735 if (!Attr.isValid() || !Attr.isStringAttribute())
8736 return {0, ThreadLimit};
8737 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8738 int32_t LB, UB;
8739 if (!llvm::to_integer(UBStr, UB, 10))
8740 return {0, ThreadLimit};
8741 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8742 if (!llvm::to_integer(LBStr, LB, 10))
8743 return {0, UB};
8744 return {LB, UB};
8745 }
8746
8747 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8748 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8749 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8750 }
8751 return {0, ThreadLimit};
8752}
8753
8755 Function &Kernel, int32_t LB,
8756 int32_t UB) {
8757 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8758
8759 if (T.isAMDGPU()) {
8760 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8761 llvm::utostr(LB) + "," + llvm::utostr(UB));
8762 return;
8763 }
8764
8766}
8767
8768std::pair<int32_t, int32_t>
8770 // TODO: Read from backend annotations if available.
8771 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8772}
8773
8775 int32_t LB, int32_t UB) {
8776 if (UB > 0) {
8777 if (T.isNVPTX())
8779 if (T.isAMDGPU())
8780 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8781 }
8782
8783 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8784}
8785
8786void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8787 Function *OutlinedFn) {
8788 if (Config.isTargetDevice()) {
8790 // TODO: Determine if DSO local can be set to true.
8791 OutlinedFn->setDSOLocal(false);
8793 if (T.isAMDGCN())
8795 else if (T.isNVPTX())
8797 else if (T.isSPIRV())
8799 }
8800}
8801
8802Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8803 StringRef EntryFnIDName) {
8804 if (Config.isTargetDevice()) {
8805 assert(OutlinedFn && "The outlined function must exist if embedded");
8806 return OutlinedFn;
8807 }
8808
8809 return new GlobalVariable(
8810 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8811 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8812}
8813
8814Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8815 StringRef EntryFnName) {
8816 if (OutlinedFn)
8817 return OutlinedFn;
8818
8819 assert(!M.getGlobalVariable(EntryFnName, true) &&
8820 "Named kernel already exists?");
8821 return new GlobalVariable(
8822 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8823 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8824}
8825
8827 TargetRegionEntryInfo &EntryInfo,
8828 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8829 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8830
8831 SmallString<64> EntryFnName;
8832 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8833
8834 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8835 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8836 if (!CBResult)
8837 return CBResult.takeError();
8838 OutlinedFn = *CBResult;
8839 } else {
8840 OutlinedFn = nullptr;
8841 }
8842
8843 // If this target outline function is not an offload entry, we don't need to
8844 // register it. This may be in the case of a false if clause, or if there are
8845 // no OpenMP targets.
8846 if (!IsOffloadEntry)
8847 return Error::success();
8848
8849 std::string EntryFnIDName =
8850 Config.isTargetDevice()
8851 ? std::string(EntryFnName)
8852 : createPlatformSpecificName({EntryFnName, "region_id"});
8853
8854 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8855 EntryFnName, EntryFnIDName);
8856 return Error::success();
8857}
8858
8860 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8861 StringRef EntryFnName, StringRef EntryFnIDName) {
8862 if (OutlinedFn)
8863 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8864 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8865 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8866 OffloadInfoManager.registerTargetRegionEntryInfo(
8867 EntryInfo, EntryAddr, OutlinedFnID,
8869 return OutlinedFnID;
8870}
8871
8873 const LocationDescription &Loc, InsertPointTy AllocaIP,
8874 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8875 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8876 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8877 omp::RuntimeFunction *MapperFunc,
8879 BodyGenTy BodyGenType)>
8880 BodyGenCB,
8881 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8882 if (!updateToLocation(Loc))
8883 return InsertPointTy();
8884
8885 Builder.restoreIP(CodeGenIP);
8886
8887 bool IsStandAlone = !BodyGenCB;
8888 MapInfosTy *MapInfo;
8889 // Generate the code for the opening of the data environment. Capture all the
8890 // arguments of the runtime call by reference because they are used in the
8891 // closing of the region.
8892 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8893 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8894 MapInfo = &GenMapInfoCB(Builder.saveIP());
8895 if (Error Err = emitOffloadingArrays(
8896 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
8897 /*IsNonContiguous=*/true, DeviceAddrCB))
8898 return Err;
8899
8900 TargetDataRTArgs RTArgs;
8902
8903 // Emit the number of elements in the offloading arrays.
8904 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8905
8906 // Source location for the ident struct
8907 if (!SrcLocInfo) {
8908 uint32_t SrcLocStrSize;
8909 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8910 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8911 }
8912
8913 SmallVector<llvm::Value *, 13> OffloadingArgs = {
8914 SrcLocInfo, DeviceID,
8915 PointerNum, RTArgs.BasePointersArray,
8916 RTArgs.PointersArray, RTArgs.SizesArray,
8917 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8918 RTArgs.MappersArray};
8919
8920 if (IsStandAlone) {
8921 assert(MapperFunc && "MapperFunc missing for standalone target data");
8922
8923 auto TaskBodyCB = [&](Value *, Value *,
8925 if (Info.HasNoWait) {
8926 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
8930 }
8931
8933 OffloadingArgs);
8934
8935 if (Info.HasNoWait) {
8936 BasicBlock *OffloadContBlock =
8937 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
8938 Function *CurFn = Builder.GetInsertBlock()->getParent();
8939 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
8940 Builder.restoreIP(Builder.saveIP());
8941 }
8942 return Error::success();
8943 };
8944
8945 bool RequiresOuterTargetTask = Info.HasNoWait;
8946 if (!RequiresOuterTargetTask)
8947 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
8948 /*TargetTaskAllocaIP=*/{}));
8949 else
8950 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
8951 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
8952 } else {
8953 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
8954 omp::OMPRTL___tgt_target_data_begin_mapper);
8955
8956 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
8957
8958 for (auto DeviceMap : Info.DevicePtrInfoMap) {
8959 if (isa<AllocaInst>(DeviceMap.second.second)) {
8960 auto *LI =
8961 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
8962 Builder.CreateStore(LI, DeviceMap.second.second);
8963 }
8964 }
8965
8966 // If device pointer privatization is required, emit the body of the
8967 // region here. It will have to be duplicated: with and without
8968 // privatization.
8969 InsertPointOrErrorTy AfterIP =
8970 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
8971 if (!AfterIP)
8972 return AfterIP.takeError();
8973 Builder.restoreIP(*AfterIP);
8974 }
8975 return Error::success();
8976 };
8977
8978 // If we need device pointer privatization, we need to emit the body of the
8979 // region with no privatization in the 'else' branch of the conditional.
8980 // Otherwise, we don't have to do anything.
8981 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8982 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8983 InsertPointOrErrorTy AfterIP =
8984 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
8985 if (!AfterIP)
8986 return AfterIP.takeError();
8987 Builder.restoreIP(*AfterIP);
8988 return Error::success();
8989 };
8990
8991 // Generate code for the closing of the data region.
8992 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8993 ArrayRef<BasicBlock *> DeallocBlocks) {
8994 TargetDataRTArgs RTArgs;
8995 Info.EmitDebug = !MapInfo->Names.empty();
8996 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
8997
8998 // Emit the number of elements in the offloading arrays.
8999 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9000
9001 // Source location for the ident struct
9002 if (!SrcLocInfo) {
9003 uint32_t SrcLocStrSize;
9004 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9005 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9006 }
9007
9008 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9009 PointerNum, RTArgs.BasePointersArray,
9010 RTArgs.PointersArray, RTArgs.SizesArray,
9011 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9012 RTArgs.MappersArray};
9013 Function *EndMapperFunc =
9014 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9015
9016 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9017 return Error::success();
9018 };
9019
9020 // We don't have to do anything to close the region if the if clause evaluates
9021 // to false.
9022 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9023 ArrayRef<BasicBlock *> DeallocBlocks) {
9024 return Error::success();
9025 };
9026
9027 Error Err = [&]() -> Error {
9028 if (BodyGenCB) {
9029 Error Err = [&]() {
9030 if (IfCond)
9031 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9032 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9033 }();
9034
9035 if (Err)
9036 return Err;
9037
9038 // If we don't require privatization of device pointers, we emit the body
9039 // in between the runtime calls. This avoids duplicating the body code.
9040 InsertPointOrErrorTy AfterIP =
9041 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9042 if (!AfterIP)
9043 return AfterIP.takeError();
9044 restoreIPandDebugLoc(Builder, *AfterIP);
9045
9046 if (IfCond)
9047 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9048 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9049 }
9050 if (IfCond)
9051 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9052 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9053 }();
9054
9055 if (Err)
9056 return Err;
9057
9058 return Builder.saveIP();
9059}
9060
9063 bool IsGPUDistribute) {
9064 assert((IVSize == 32 || IVSize == 64) &&
9065 "IV size is not compatible with the omp runtime");
9066 RuntimeFunction Name;
9067 if (IsGPUDistribute)
9068 Name = IVSize == 32
9069 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9070 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9071 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9072 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9073 else
9074 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9075 : omp::OMPRTL___kmpc_for_static_init_4u)
9076 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9077 : omp::OMPRTL___kmpc_for_static_init_8u);
9078
9079 return getOrCreateRuntimeFunction(M, Name);
9080}
9081
9083 bool IVSigned) {
9084 assert((IVSize == 32 || IVSize == 64) &&
9085 "IV size is not compatible with the omp runtime");
9086 RuntimeFunction Name = IVSize == 32
9087 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9088 : omp::OMPRTL___kmpc_dispatch_init_4u)
9089 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9090 : omp::OMPRTL___kmpc_dispatch_init_8u);
9091
9092 return getOrCreateRuntimeFunction(M, Name);
9093}
9094
9096 bool IVSigned) {
9097 assert((IVSize == 32 || IVSize == 64) &&
9098 "IV size is not compatible with the omp runtime");
9099 RuntimeFunction Name = IVSize == 32
9100 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9101 : omp::OMPRTL___kmpc_dispatch_next_4u)
9102 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9103 : omp::OMPRTL___kmpc_dispatch_next_8u);
9104
9105 return getOrCreateRuntimeFunction(M, Name);
9106}
9107
9109 bool IVSigned) {
9110 assert((IVSize == 32 || IVSize == 64) &&
9111 "IV size is not compatible with the omp runtime");
9112 RuntimeFunction Name = IVSize == 32
9113 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9114 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9115 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9116 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9117
9118 return getOrCreateRuntimeFunction(M, Name);
9119}
9120
9122 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9123}
9124
9126 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9127 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9128
9129 DISubprogram *NewSP = Func->getSubprogram();
9130 if (!NewSP)
9131 return;
9132
9134
9135 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9136 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9137 // Only use cached variable if the arg number matches. This is important
9138 // so that DIVariable created for privatized variables are not discarded.
9139 if (NewVar && (arg == NewVar->getArg()))
9140 return NewVar;
9141
9143 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9144 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9145 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9146 return NewVar;
9147 };
9148
9149 auto UpdateDebugRecord = [&](auto *DR) {
9150 DILocalVariable *OldVar = DR->getVariable();
9151 unsigned ArgNo = 0;
9152 for (auto Loc : DR->location_ops()) {
9153 auto Iter = ValueReplacementMap.find(Loc);
9154 if (Iter != ValueReplacementMap.end()) {
9155 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9156 ArgNo = std::get<1>(Iter->second) + 1;
9157 }
9158 }
9159 if (ArgNo != 0)
9160 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9161 };
9162
9164 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9165 if (DVR->getNumVariableLocationOps() != 1u) {
9166 DVR->setKillLocation();
9167 return;
9168 }
9169 Value *Loc = DVR->getVariableLocationOp(0u);
9170 BasicBlock *CurBB = DVR->getParent();
9171 BasicBlock *RequiredBB = nullptr;
9172
9173 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9174 RequiredBB = LocInst->getParent();
9175 else if (isa<llvm::Argument>(Loc))
9176 RequiredBB = &DVR->getFunction()->getEntryBlock();
9177
9178 if (RequiredBB && RequiredBB != CurBB) {
9179 assert(!RequiredBB->empty());
9180 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9181 RequiredBB->back().getIterator());
9182 DVRsToDelete.push_back(DVR);
9183 }
9184 };
9185
9186 // The location and scope of variable intrinsics and records still point to
9187 // the parent function of the target region. Update them.
9188 for (Instruction &I : instructions(Func)) {
9190 "Unexpected debug intrinsic");
9191 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9192 UpdateDebugRecord(&DVR);
9193 MoveDebugRecordToCorrectBlock(&DVR);
9194 }
9195 }
9196 for (auto *DVR : DVRsToDelete)
9197 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9198 // An extra argument is passed to the device. Create the debug data for it.
9199 if (OMPBuilder.Config.isTargetDevice()) {
9200 DICompileUnit *CU = NewSP->getUnit();
9201 Module *M = Func->getParent();
9202 DIBuilder DB(*M, true, CU);
9203 DIType *VoidPtrTy =
9204 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9205 unsigned ArgNo = Func->arg_size();
9206 DILocalVariable *Var = DB.createParameterVariable(
9207 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9208 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9209 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9210 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9211 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9212 &(*Func->begin()));
9213 }
9214}
9215
9217 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9218 return cast<Operator>(V)->getOperand(0);
9219 return V;
9220}
9221
9223 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9225 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9228 SmallVector<Type *> ParameterTypes;
9229 if (OMPBuilder.Config.isTargetDevice()) {
9230 // All parameters to target devices are passed as pointers
9231 // or i64. This assumes 64-bit address spaces/pointers.
9232 for (auto &Arg : Inputs)
9233 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9234 ? Arg->getType()
9235 : Type::getInt64Ty(Builder.getContext()));
9236 } else {
9237 for (auto &Arg : Inputs)
9238 ParameterTypes.push_back(Arg->getType());
9239 }
9240
9241 // The implicit dyn_ptr argument is always the last parameter on both host
9242 // and device so the argument counts match without runtime manipulation.
9243 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9244 ParameterTypes.push_back(PtrTy);
9245
9246 auto BB = Builder.GetInsertBlock();
9247 auto M = BB->getModule();
9248 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9249 /*isVarArg*/ false);
9250 auto Func =
9251 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9252
9253 // Forward target-cpu and target-features function attributes from the
9254 // original function to the new outlined function.
9255 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9256
9257 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9258 if (TargetCpuAttr.isStringAttribute())
9259 Func->addFnAttr(TargetCpuAttr);
9260
9261 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9262 if (TargetFeaturesAttr.isStringAttribute())
9263 Func->addFnAttr(TargetFeaturesAttr);
9264
9265 if (OMPBuilder.Config.isTargetDevice()) {
9266 Value *ExecMode =
9267 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9268 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9269 }
9270
9271 // Save insert point.
9272 IRBuilder<>::InsertPointGuard IPG(Builder);
9273 // We will generate the entries in the outlined function but the debug
9274 // location may still be pointing to the parent function. Reset it now.
9275 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9276
9277 // Generate the region into the function.
9278 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9279 Builder.SetInsertPoint(EntryBB);
9280
9281 // Insert target init call in the device compilation pass.
9282 if (OMPBuilder.Config.isTargetDevice())
9283 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9284
9285 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9286
9287 // As we embed the user code in the middle of our target region after we
9288 // generate entry code, we must move what allocas we can into the entry
9289 // block to avoid possible breaking optimisations for device
9290 if (OMPBuilder.Config.isTargetDevice())
9292
9293 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9294 BasicBlock *OutlinedBodyBB =
9295 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9297 Builder.saveIP(),
9298 OpenMPIRBuilder::InsertPointTy(OutlinedBodyBB, OutlinedBodyBB->begin()),
9299 ExitBB);
9300 if (!AfterIP)
9301 return AfterIP.takeError();
9302 Builder.SetInsertPoint(ExitBB);
9303
9304 // Insert target deinit call in the device compilation pass.
9305 if (OMPBuilder.Config.isTargetDevice())
9306 OMPBuilder.createTargetDeinit(Builder);
9307
9308 // Insert return instruction.
9309 Builder.CreateRetVoid();
9310
9311 // New Alloca IP at entry point of created device function.
9312 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9313 auto AllocaIP = Builder.saveIP();
9314
9315 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());
9316
9317 // Do not include the artificial dyn_ptr argument.
9318 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9319
9321
9322 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9323 // Things like GEP's can come in the form of Constants. Constants and
9324 // ConstantExpr's do not have access to the knowledge of what they're
9325 // contained in, so we must dig a little to find an instruction so we
9326 // can tell if they're used inside of the function we're outlining. We
9327 // also replace the original constant expression with a new instruction
9328 // equivalent; an instruction as it allows easy modification in the
9329 // following loop, as we can now know the constant (instruction) is
9330 // owned by our target function and replaceUsesOfWith can now be invoked
9331 // on it (cannot do this with constants it seems). A brand new one also
9332 // allows us to be cautious as it is perhaps possible the old expression
9333 // was used inside of the function but exists and is used externally
9334 // (unlikely by the nature of a Constant, but still).
9335 // NOTE: We cannot remove dead constants that have been rewritten to
9336 // instructions at this stage, we run the risk of breaking later lowering
9337 // by doing so as we could still be in the process of lowering the module
9338 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9339 // constants we have created rewritten versions of.
9340 if (auto *Const = dyn_cast<Constant>(Input))
9341 convertUsersOfConstantsToInstructions(Const, Func, false);
9342
9343 // Collect users before iterating over them to avoid invalidating the
9344 // iteration in case a user uses Input more than once (e.g. a call
9345 // instruction).
9346 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9347 // Collect all the instructions
9349 if (auto *Instr = dyn_cast<Instruction>(User))
9350 if (Instr->getFunction() == Func)
9351 Instr->replaceUsesOfWith(Input, InputCopy);
9352 };
9353
9354 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9355
9356 // Rewrite uses of input valus to parameters.
9357 for (auto InArg : zip(Inputs, ArgRange)) {
9358 Value *Input = std::get<0>(InArg);
9359 Argument &Arg = std::get<1>(InArg);
9360 Value *InputCopy = nullptr;
9361
9362 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9363 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(),
9364 OpenMPIRBuilder::InsertPointTy(ExitBB, ExitBB->begin()));
9365 if (!AfterIP)
9366 return AfterIP.takeError();
9367 Builder.restoreIP(*AfterIP);
9368 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9369
9370 // In certain cases a Global may be set up for replacement, however, this
9371 // Global may be used in multiple arguments to the kernel, just segmented
9372 // apart, for example, if we have a global array, that is sectioned into
9373 // multiple mappings (technically not legal in OpenMP, but there is a case
9374 // in Fortran for Common Blocks where this is neccesary), we will end up
9375 // with GEP's into this array inside the kernel, that refer to the Global
9376 // but are technically separate arguments to the kernel for all intents and
9377 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9378 // index, it will fold into an referal to the Global, if we then encounter
9379 // this folded GEP during replacement all of the references to the
9380 // Global in the kernel will be replaced with the argument we have generated
9381 // that corresponds to it, including any other GEP's that refer to the
9382 // Global that may be other arguments. This will invalidate all of the other
9383 // preceding mapped arguments that refer to the same global that may be
9384 // separate segments. To prevent this, we defer global processing until all
9385 // other processing has been performed.
9388 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9389 continue;
9390 }
9391
9393 continue;
9394
9395 ReplaceValue(Input, InputCopy, Func);
9396 }
9397
9398 // Replace all of our deferred Input values, currently just Globals.
9399 for (auto Deferred : DeferredReplacement)
9400 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9401
9402 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9403 ValueReplacementMap);
9404 return Func;
9405}
9406/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9407/// of pointers containing shared data between the parent task and the created
9408/// task.
9410 IRBuilderBase &Builder,
9411 Value *TaskWithPrivates,
9412 Type *TaskWithPrivatesTy) {
9413
9414 Type *TaskTy = OMPIRBuilder.Task;
9415 LLVMContext &Ctx = Builder.getContext();
9416 Value *TaskT =
9417 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9418 Value *Shareds = TaskT;
9419 // TaskWithPrivatesTy can be one of the following
9420 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9421 // %struct.privates }
9422 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9423 //
9424 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9425 // its first member has to be the task descriptor. TaskTy is the type of the
9426 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9427 // first member of TaskT, gives us the pointer to shared data.
9428 if (TaskWithPrivatesTy != TaskTy)
9429 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9430 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9431}
9432/// Create an entry point for a target task with the following.
9433/// It'll have the following signature
9434/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9435/// This function is called from emitTargetTask once the
9436/// code to launch the target kernel has been outlined already.
9437/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9438/// into the task structure so that the deferred target task can access this
9439/// data even after the stack frame of the generating task has been rolled
9440/// back. Offloading arrays contain base pointers, pointers, sizes etc
9441/// of the data that the target kernel will access. These in effect are the
9442/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9444 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9445 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9446 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9447
9448 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9449 // This is because PrivatesTy is the type of the structure in which
9450 // we pass the offloading arrays to the deferred target task.
9451 assert((!NumOffloadingArrays || PrivatesTy) &&
9452 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9453 "to privatize");
9454
9455 Module &M = OMPBuilder.M;
9456 // KernelLaunchFunction is the target launch function, i.e.
9457 // the function that sets up kernel arguments and calls
9458 // __tgt_target_kernel to launch the kernel on the device.
9459 //
9460 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9461
9462 // StaleCI is the CallInst which is the call to the outlined
9463 // target kernel launch function. If there are local live-in values
9464 // that the outlined function uses then these are aggregated into a structure
9465 // which is passed as the second argument. If there are no local live-in
9466 // values or if all values used by the outlined kernel are global variables,
9467 // then there's only one argument, the threadID. So, StaleCI can be
9468 //
9469 // %structArg = alloca { ptr, ptr }, align 8
9470 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9471 // store ptr %20, ptr %gep_, align 8
9472 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9473 // store ptr %21, ptr %gep_8, align 8
9474 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9475 //
9476 // OR
9477 //
9478 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9480 StaleCI->getIterator());
9481
9482 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9483
9484 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9485 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9486 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9487
9488 auto ProxyFnTy =
9489 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9490 /* isVarArg */ false);
9491 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9492 ".omp_target_task_proxy_func", M);
9493 Value *ThreadId = ProxyFn->getArg(0);
9494 Value *TaskWithPrivates = ProxyFn->getArg(1);
9495 ThreadId->setName("thread.id");
9496 TaskWithPrivates->setName("task");
9497
9498 bool HasShareds = SharedArgsOperandNo > 0;
9499 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9500 BasicBlock *EntryBB =
9501 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9502 Builder.SetInsertPoint(EntryBB);
9503
9504 SmallVector<Value *> KernelLaunchArgs;
9505 KernelLaunchArgs.reserve(StaleCI->arg_size());
9506 KernelLaunchArgs.push_back(ThreadId);
9507
9508 if (HasOffloadingArrays) {
9509 assert(TaskTy != TaskWithPrivatesTy &&
9510 "If there are offloading arrays to pass to the target"
9511 "TaskTy cannot be the same as TaskWithPrivatesTy");
9512 (void)TaskTy;
9513 Value *Privates =
9514 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9515 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9516 KernelLaunchArgs.push_back(
9517 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9518 }
9519
9520 if (HasShareds) {
9521 auto *ArgStructAlloca =
9522 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9523 assert(ArgStructAlloca &&
9524 "Unable to find the alloca instruction corresponding to arguments "
9525 "for extracted function");
9526 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9527 std::optional<TypeSize> ArgAllocSize =
9528 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9529 assert(ArgStructType && ArgAllocSize &&
9530 "Unable to determine size of arguments for extracted function");
9531 uint64_t StructSize = ArgAllocSize->getFixedValue();
9532
9533 AllocaInst *NewArgStructAlloca =
9534 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9535
9536 Value *SharedsSize = Builder.getInt64(StructSize);
9537
9539 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9540
9541 Builder.CreateMemCpy(
9542 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9543 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9544 KernelLaunchArgs.push_back(NewArgStructAlloca);
9545 }
9546 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9547 Builder.CreateRetVoid();
9548 return ProxyFn;
9549}
9551
9552 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9553 return GEP->getSourceElementType();
9554 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9555 return Alloca->getAllocatedType();
9556
9557 llvm_unreachable("Unhandled Instruction type");
9558 return nullptr;
9559}
9560// This function returns a struct that has at most two members.
9561// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9562// descriptor. The second member, if needed, is a struct containing arrays
9563// that need to be passed to the offloaded target kernel. For example,
9564// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9565// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9566// respectively, then the types created by this function are
9567//
9568// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9569// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9570// %struct.privates }
9571// %struct.task_with_privates is returned by this function.
9572// If there aren't any offloading arrays to pass to the target kernel,
9573// %struct.kmp_task_ompbuilder_t is returned.
9574static StructType *
9576 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9577
9578 if (OffloadingArraysToPrivatize.empty())
9579 return OMPIRBuilder.Task;
9580
9581 SmallVector<Type *, 4> StructFieldTypes;
9582 for (Value *V : OffloadingArraysToPrivatize) {
9583 assert(V->getType()->isPointerTy() &&
9584 "Expected pointer to array to privatize. Got a non-pointer value "
9585 "instead");
9586 Type *ArrayTy = getOffloadingArrayType(V);
9587 assert(ArrayTy && "ArrayType cannot be nullptr");
9588 StructFieldTypes.push_back(ArrayTy);
9589 }
9590 StructType *PrivatesStructTy =
9591 StructType::create(StructFieldTypes, "struct.privates");
9592 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9593 "struct.task_with_privates");
9594}
9596 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9597 TargetRegionEntryInfo &EntryInfo,
9599 Function *&OutlinedFn, Constant *&OutlinedFnID,
9603
9604 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9605 [&](StringRef EntryFnName) {
9606 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9607 EntryFnName, Inputs, CBFunc,
9608 ArgAccessorFuncCB);
9609 };
9610
9611 return OMPBuilder.emitTargetRegionFunction(
9612 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9613 OutlinedFnID);
9614}
9615
9617 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9619 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9620 bool HasNoWait) {
9621
9622 // The following explains the code-gen scenario for the `target` directive. A
9623 // similar scneario is followed for other device-related directives (e.g.
9624 // `target enter data`) but in similar fashion since we only need to emit task
9625 // that encapsulates the proper runtime call.
9626 //
9627 // When we arrive at this function, the target region itself has been
9628 // outlined into the function OutlinedFn.
9629 // So at ths point, for
9630 // --------------------------------------------------------------
9631 // void user_code_that_offloads(...) {
9632 // omp target depend(..) map(from:a) map(to:b) private(i)
9633 // do i = 1, 10
9634 // a(i) = b(i) + n
9635 // }
9636 //
9637 // --------------------------------------------------------------
9638 //
9639 // we have
9640 //
9641 // --------------------------------------------------------------
9642 //
9643 // void user_code_that_offloads(...) {
9644 // %.offload_baseptrs = alloca [2 x ptr], align 8
9645 // %.offload_ptrs = alloca [2 x ptr], align 8
9646 // %.offload_mappers = alloca [2 x ptr], align 8
9647 // ;; target region has been outlined and now we need to
9648 // ;; offload to it via a target task.
9649 // }
9650 // void outlined_device_function(ptr a, ptr b, ptr n) {
9651 // n = *n_ptr;
9652 // do i = 1, 10
9653 // a(i) = b(i) + n
9654 // }
9655 //
9656 // We have to now do the following
9657 // (i) Make an offloading call to outlined_device_function using the OpenMP
9658 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9659 // emitted by emitKernelLaunch
9660 // (ii) Create a task entry point function that calls kernel_launch_function
9661 // and is the entry point for the target task. See
9662 // '@.omp_target_task_proxy_func in the pseudocode below.
9663 // (iii) Create a task with the task entry point created in (ii)
9664 //
9665 // That is we create the following
9666 // struct task_with_privates {
9667 // struct kmp_task_ompbuilder_t task_struct;
9668 // struct privates {
9669 // [2 x ptr] ; baseptrs
9670 // [2 x ptr] ; ptrs
9671 // [2 x i64] ; sizes
9672 // }
9673 // }
9674 // void user_code_that_offloads(...) {
9675 // %.offload_baseptrs = alloca [2 x ptr], align 8
9676 // %.offload_ptrs = alloca [2 x ptr], align 8
9677 // %.offload_sizes = alloca [2 x i64], align 8
9678 //
9679 // %structArg = alloca { ptr, ptr, ptr }, align 8
9680 // %strucArg[0] = a
9681 // %strucArg[1] = b
9682 // %strucArg[2] = &n
9683 //
9684 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9685 // sizeof(kmp_task_ompbuilder_t),
9686 // sizeof(structArg),
9687 // @.omp_target_task_proxy_func,
9688 // ...)
9689 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9690 // sizeof(structArg))
9691 // memcpy(target_task_with_privates->privates->baseptrs,
9692 // offload_baseptrs, sizeof(offload_baseptrs)
9693 // memcpy(target_task_with_privates->privates->ptrs,
9694 // offload_ptrs, sizeof(offload_ptrs)
9695 // memcpy(target_task_with_privates->privates->sizes,
9696 // offload_sizes, sizeof(offload_sizes)
9697 // dependencies_array = ...
9698 // ;; if nowait not present
9699 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9700 // call @__kmpc_omp_task_begin_if0(...)
9701 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9702 // %target_task_with_privates)
9703 // call @__kmpc_omp_task_complete_if0(...)
9704 // }
9705 //
9706 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9707 // ptr %task) {
9708 // %structArg = alloca {ptr, ptr, ptr}
9709 // %task_ptr = getelementptr(%task, 0, 0)
9710 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9711 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9712 //
9713 // %offloading_arrays = getelementptr(%task, 0, 1)
9714 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9715 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9716 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9717 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9718 // %offload_sizes, %structArg)
9719 // }
9720 //
9721 // We need the proxy function because the signature of the task entry point
9722 // expected by kmpc_omp_task is always the same and will be different from
9723 // that of the kernel_launch function.
9724 //
9725 // kernel_launch_function is generated by emitKernelLaunch and has the
9726 // always_inline attribute. For this example, it'll look like so:
9727 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9728 // %offload_sizes, %structArg) alwaysinline {
9729 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9730 // ; load aggregated data from %structArg
9731 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9732 // ; offload_sizes
9733 // call i32 @__tgt_target_kernel(...,
9734 // outlined_device_function,
9735 // ptr %kernel_args)
9736 // }
9737 // void outlined_device_function(ptr a, ptr b, ptr n) {
9738 // n = *n_ptr;
9739 // do i = 1, 10
9740 // a(i) = b(i) + n
9741 // }
9742 //
9743 BasicBlock *TargetTaskBodyBB =
9744 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9745 BasicBlock *TargetTaskAllocaBB =
9746 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9747
9748 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB,
9749 TargetTaskAllocaBB->begin());
9750 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB, TargetTaskBodyBB->begin());
9751
9752 auto OI = std::make_unique<OutlineInfo>();
9753 OI->EntryBB = TargetTaskAllocaBB;
9754 OI->OuterAllocBB = AllocaIP.getBlock();
9755
9756 // Add the thread ID argument.
9758 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9759 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9760
9761 // Generate the task body which will subsequently be outlined.
9762 Builder.restoreIP(TargetTaskBodyIP);
9763 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9764 return Err;
9765
9766 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9767 // it is given. These blocks are enumerated by
9768 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9769 // to be outside the region. In other words, OI.ExitBlock is expected to be
9770 // the start of the region after the outlining. We used to set OI.ExitBlock
9771 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9772 // except when the task body is a single basic block. In that case,
9773 // OI.ExitBlock is set to the single task body block and will get left out of
9774 // the outlining process. So, simply create a new empty block to which we
9775 // uncoditionally branch from where TaskBodyCB left off
9776 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9777 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9778 /*IsFinished=*/true);
9779
9780 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9781 bool NeedsTargetTask = HasNoWait && DeviceID;
9782 if (NeedsTargetTask) {
9783 for (auto *V :
9784 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9785 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9786 RTArgs.SizesArray}) {
9788 OffloadingArraysToPrivatize.push_back(V);
9789 OI->ExcludeArgsFromAggregate.push_back(V);
9790 }
9791 }
9792 }
9793 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9794 DeviceID, OffloadingArraysToPrivatize](
9795 Function &OutlinedFn) mutable {
9796 assert(OutlinedFn.hasOneUse() &&
9797 "there must be a single user for the outlined function");
9798
9799 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9800
9801 // The first argument of StaleCI is always the thread id.
9802 // The next few arguments are the pointers to offloading arrays
9803 // if any. (see OffloadingArraysToPrivatize)
9804 // Finally, all other local values that are live-in into the outlined region
9805 // end up in a structure whose pointer is passed as the last argument. This
9806 // piece of data is passed in the "shared" field of the task structure. So,
9807 // we know we have to pass shareds to the task if the number of arguments is
9808 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9809 // thread id. Further, for safety, we assert that the number of arguments of
9810 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9811 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9812 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9813 assert((!HasShareds ||
9814 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9815 "Wrong number of arguments for StaleCI when shareds are present");
9816 int SharedArgOperandNo =
9817 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9818
9819 StructType *TaskWithPrivatesTy =
9820 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9821 StructType *PrivatesTy = nullptr;
9822
9823 if (!OffloadingArraysToPrivatize.empty())
9824 PrivatesTy =
9825 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9826
9828 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9829 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9830
9831 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9832 << "\n");
9833
9834 Builder.SetInsertPoint(StaleCI);
9835
9836 // Gather the arguments for emitting the runtime call.
9837 uint32_t SrcLocStrSize;
9838 Constant *SrcLocStr =
9840 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9841
9842 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9843 //
9844 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9845 // the DeviceID to the deferred task and also since
9846 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9847 Function *TaskAllocFn =
9848 !NeedsTargetTask
9849 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9851 OMPRTL___kmpc_omp_target_task_alloc);
9852
9853 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9854 // call.
9855 Value *ThreadID = getOrCreateThreadID(Ident);
9856
9857 // Argument - `sizeof_kmp_task_t` (TaskSize)
9858 // Tasksize refers to the size in bytes of kmp_task_t data structure
9859 // plus any other data to be passed to the target task, if any, which
9860 // is packed into a struct. kmp_task_t and the struct so created are
9861 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9862 Value *TaskSize = Builder.getInt64(
9863 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9864
9865 // Argument - `sizeof_shareds` (SharedsSize)
9866 // SharedsSize refers to the shareds array size in the kmp_task_t data
9867 // structure.
9868 Value *SharedsSize = Builder.getInt64(0);
9869 if (HasShareds) {
9870 auto *ArgStructAlloca =
9871 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9872 assert(ArgStructAlloca &&
9873 "Unable to find the alloca instruction corresponding to arguments "
9874 "for extracted function");
9875 std::optional<TypeSize> ArgAllocSize =
9876 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9877 assert(ArgAllocSize &&
9878 "Unable to determine size of arguments for extracted function");
9879 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9880 }
9881
9882 // Argument - `flags`
9883 // Task is tied iff (Flags & 1) == 1.
9884 // Task is untied iff (Flags & 1) == 0.
9885 // Task is final iff (Flags & 2) == 2.
9886 // Task is not final iff (Flags & 2) == 0.
9887 // A target task is not final and is untied.
9888 Value *Flags = Builder.getInt32(0);
9889
9890 // Emit the @__kmpc_omp_task_alloc runtime call
9891 // The runtime call returns a pointer to an area where the task captured
9892 // variables must be copied before the task is run (TaskData)
9893 CallInst *TaskData = nullptr;
9894
9895 SmallVector<llvm::Value *> TaskAllocArgs = {
9896 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
9897 /*flags=*/Flags,
9898 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
9899 /*task_func=*/ProxyFn};
9900
9901 if (NeedsTargetTask) {
9902 assert(DeviceID && "Expected non-empty device ID.");
9903 TaskAllocArgs.push_back(DeviceID);
9904 }
9905
9906 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
9907
9908 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
9909 if (HasShareds) {
9910 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
9912 *this, Builder, TaskData, TaskWithPrivatesTy);
9913 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
9914 SharedsSize);
9915 }
9916 if (!OffloadingArraysToPrivatize.empty()) {
9917 Value *Privates =
9918 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
9919 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
9920 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
9921 [[maybe_unused]] Type *ArrayType =
9922 getOffloadingArrayType(PtrToPrivatize);
9923 assert(ArrayType && "ArrayType cannot be nullptr");
9924
9925 Type *ElementType = PrivatesTy->getElementType(i);
9926 assert(ElementType == ArrayType &&
9927 "ElementType should match ArrayType");
9928 (void)ArrayType;
9929
9930 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
9931 Builder.CreateMemCpy(
9932 Dst, Alignment, PtrToPrivatize, Alignment,
9933 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
9934 }
9935 }
9936
9937 Value *DepArray = nullptr;
9938 Value *NumDeps = nullptr;
9939 if (Dependencies.DepArray) {
9940 DepArray = Dependencies.DepArray;
9941 NumDeps = Dependencies.NumDeps;
9942 } else if (!Dependencies.Deps.empty()) {
9943 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
9944 NumDeps = Builder.getInt32(Dependencies.Deps.size());
9945 }
9946
9947 // ---------------------------------------------------------------
9948 // V5.2 13.8 target construct
9949 // If the nowait clause is present, execution of the target task
9950 // may be deferred. If the nowait clause is not present, the target task is
9951 // an included task.
9952 // ---------------------------------------------------------------
9953 // The above means that the lack of a nowait on the target construct
9954 // translates to '#pragma omp task if(0)'
9955 if (!NeedsTargetTask) {
9956 if (DepArray) {
9957 Function *TaskWaitFn =
9958 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
9960 TaskWaitFn,
9961 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
9962 /*ndeps=*/NumDeps,
9963 /*dep_list=*/DepArray,
9964 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
9965 /*noalias_dep_list=*/
9967 }
9968 // Included task.
9969 Function *TaskBeginFn =
9970 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
9971 Function *TaskCompleteFn =
9972 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
9973 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
9974 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
9975 CI->setDebugLoc(StaleCI->getDebugLoc());
9976 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
9977 } else if (DepArray) {
9978 // HasNoWait - meaning the task may be deferred. Call
9979 // __kmpc_omp_task_with_deps if there are dependencies,
9980 // else call __kmpc_omp_task
9981 Function *TaskFn =
9982 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
9984 TaskFn,
9985 {Ident, ThreadID, TaskData, NumDeps, DepArray,
9986 ConstantInt::get(Builder.getInt32Ty(), 0),
9988 } else {
9989 // Emit the @__kmpc_omp_task runtime call to spawn the task
9990 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
9991 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
9992 }
9993
9994 Builder.ClearInsertionPoint();
9995 StaleCI->eraseFromParent();
9996 for (Instruction *I : llvm::reverse(ToBeDeleted))
9997 I->eraseFromParent();
9998 };
9999 addOutlineInfo(std::move(OI));
10000
10001 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
10002 << *(Builder.GetInsertBlock()) << "\n");
10003 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
10004 << *(Builder.GetInsertBlock()->getParent()->getParent())
10005 << "\n");
10006 return Builder.saveIP();
10007}
10008
10010 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10011 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10012 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10013 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10014 if (Error Err =
10015 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10016 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10017 return Err;
10018 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10019 return Error::success();
10020}
10021
10022static void emitTargetCall(
10023 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
10028 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10032 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10033 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10034 // Generate a function call to the host fallback implementation of the target
10035 // region. This is called by the host when no offload entry was generated for
10036 // the target region and when the offloading call fails at runtime.
10037 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10039 Builder.restoreIP(IP);
10040 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10041 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10042 FallbackArgs.push_back(
10043 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10044 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10045 return Builder.saveIP();
10046 };
10047
10048 bool HasDependencies = !Dependencies.empty();
10049 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10050
10052
10053 auto TaskBodyCB =
10054 [&](Value *DeviceID, Value *RTLoc,
10055 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10056 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10057 // produce any.
10059 // emitKernelLaunch makes the necessary runtime call to offload the
10060 // kernel. We then outline all that code into a separate function
10061 // ('kernel_launch_function' in the pseudo code above). This function is
10062 // then called by the target task proxy function (see
10063 // '@.omp_target_task_proxy_func' in the pseudo code above)
10064 // "@.omp_target_task_proxy_func' is generated by
10065 // emitTargetTaskProxyFunction.
10066 if (OutlinedFnID && DeviceID)
10067 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10068 EmitTargetCallFallbackCB, KArgs,
10069 DeviceID, RTLoc, TargetTaskAllocaIP);
10070
10071 // We only need to do the outlining if `DeviceID` is set to avoid calling
10072 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10073 // generating the `else` branch of an `if` clause.
10074 //
10075 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10076 // In this case, we execute the host implementation directly.
10077 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10078 }());
10079
10080 OMPBuilder.Builder.restoreIP(AfterIP);
10081 return Error::success();
10082 };
10083
10084 auto &&EmitTargetCallElse =
10085 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10087 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10088 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10089 // produce any.
10091 if (RequiresOuterTargetTask) {
10092 // Arguments that are intended to be directly forwarded to an
10093 // emitKernelLaunch call are pased as nullptr, since
10094 // OutlinedFnID=nullptr results in that call not being done.
10096 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10097 /*RTLoc=*/nullptr, AllocaIP,
10098 Dependencies, EmptyRTArgs, HasNoWait);
10099 }
10100 return EmitTargetCallFallbackCB(Builder.saveIP());
10101 }());
10102
10103 Builder.restoreIP(AfterIP);
10104 return Error::success();
10105 };
10106
10107 auto &&EmitTargetCallThen =
10108 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10110 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10111 Info.HasNoWait = HasNoWait;
10112 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10113
10115 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10116 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10117 /*IsNonContiguous=*/true,
10118 /*ForEndCall=*/false))
10119 return Err;
10120
10121 SmallVector<Value *, 3> NumTeamsC;
10122 for (auto [DefaultVal, RuntimeVal] :
10123 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10124 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10125 : Builder.getInt32(DefaultVal));
10126
10127 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10128 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10129 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10130 if (Clause)
10131 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10132 /*isSigned=*/false);
10133 return Clause;
10134 };
10135 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10136 if (Clause)
10137 Result =
10138 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10139 Result, Clause)
10140 : Clause;
10141 };
10142
10143 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10144 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10145 SmallVector<Value *, 3> NumThreadsC;
10146 Value *MaxThreadsClause =
10147 RuntimeAttrs.TeamsThreadLimit.size() == 1
10148 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads.front())
10149 : nullptr;
10150
10151 for (auto [TeamsVal, TargetVal] : zip_equal(
10152 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10153 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10154 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10155
10156 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10157 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10158
10159 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10160 }
10161
10162 unsigned NumTargetItems = Info.NumberOfPtrs;
10163 uint32_t SrcLocStrSize;
10164 Constant *SrcLocStr = OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10165 Value *RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10166 llvm::omp::IdentFlag(0), 0);
10167
10168 Value *TripCount = RuntimeAttrs.LoopTripCount
10169 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10170 Builder.getInt64Ty(),
10171 /*isSigned=*/false)
10172 : Builder.getInt64(0);
10173
10174 // Request zero groupprivate bytes by default.
10175 if (!DynCGroupMem)
10176 DynCGroupMem = Builder.getInt32(0);
10177
10179 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10180 HasNoWait, /*StrictBlocks=*/false, /*StrictThreads=*/false,
10181 DynCGroupMemFallback);
10182
10183 // Assume no error was returned because TaskBodyCB and
10184 // EmitTargetCallFallbackCB don't produce any.
10186 // The presence of certain clauses on the target directive require the
10187 // explicit generation of the target task.
10188 if (RequiresOuterTargetTask)
10189 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10190 RTLoc, AllocaIP, Dependencies,
10191 KArgs.RTArgs, Info.HasNoWait);
10192
10193 return OMPBuilder.emitKernelLaunch(
10194 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10195 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10196 }());
10197
10198 Builder.restoreIP(AfterIP);
10199 return Error::success();
10200 };
10201
10202 // If we don't have an ID for the target region, it means an offload entry
10203 // wasn't created. In this case we just run the host fallback directly and
10204 // ignore any potential 'if' clauses.
10205 if (!OutlinedFnID) {
10206 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10207 return;
10208 }
10209
10210 // If there's no 'if' clause, only generate the kernel launch code path.
10211 if (!IfCond) {
10212 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10213 return;
10214 }
10215
10216 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10217 EmitTargetCallElse, AllocaIP));
10218}
10219
10221 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10222 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10223 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10224 const TargetKernelDefaultAttrs &DefaultAttrs,
10225 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10226 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10229 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10230 bool HasNowait, Value *DynCGroupMem,
10231 OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10232
10233 if (!updateToLocation(Loc))
10234 return InsertPointTy();
10235
10236 Builder.restoreIP(CodeGenIP);
10237
10238 Function *OutlinedFn;
10239 Constant *OutlinedFnID = nullptr;
10240 // The target region is outlined into its own function. The LLVM IR for
10241 // the target region itself is generated using the callbacks CBFunc
10242 // and ArgAccessorFuncCB
10244 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10245 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB))
10246 return Err;
10247
10248 // If we are not on the target device, then we need to generate code
10249 // to make a remote call (offload) to the previously outlined function
10250 // that represents the target region. Do that now.
10251 if (!Config.isTargetDevice())
10252 emitTargetCall(*this, Builder, AllocaIP, DeallocBlocks, Info, DefaultAttrs,
10253 RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID, Inputs,
10254 GenMapInfoCB, CustomMapperCB, Dependencies, HasNowait,
10255 DynCGroupMem, DynCGroupMemFallback);
10256 return Builder.saveIP();
10257}
10258
10259std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10260 StringRef FirstSeparator,
10261 StringRef Separator) {
10262 SmallString<128> Buffer;
10263 llvm::raw_svector_ostream OS(Buffer);
10264 StringRef Sep = FirstSeparator;
10265 for (StringRef Part : Parts) {
10266 OS << Sep << Part;
10267 Sep = Separator;
10268 }
10269 return OS.str().str();
10270}
10271
10272std::string
10274 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10275 Config.separator());
10276}
10277
10279 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10280 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10281 if (Elem.second) {
10282 assert(Elem.second->getValueType() == Ty &&
10283 "OMP internal variable has different type than requested");
10284 } else {
10285 // TODO: investigate the appropriate linkage type used for the global
10286 // variable for possibly changing that to internal or private, or maybe
10287 // create different versions of the function for different OMP internal
10288 // variables.
10289 const DataLayout &DL = M.getDataLayout();
10290 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10291 // default global AS is 1.
10292 // See double-target-call-with-declare-target.f90 and
10293 // declare-target-vars-in-target-region.f90 libomptarget
10294 // tests.
10295 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10296 : M.getTargetTriple().isAMDGPU()
10297 ? 0
10298 : DL.getDefaultGlobalsAddressSpace();
10299 auto Linkage = this->M.getTargetTriple().isWasm()
10302 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10303 Constant::getNullValue(Ty), Elem.first(),
10304 /*InsertBefore=*/nullptr,
10305 GlobalValue::NotThreadLocal, AddressSpaceVal);
10306 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10307 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10308 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10309 Elem.second = GV;
10310 }
10311
10312 return Elem.second;
10313}
10314
10315Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10316 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10317 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10318 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10319}
10320
10322 LLVMContext &Ctx = Builder.getContext();
10323 Value *Null =
10324 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10325 Value *SizeGep =
10326 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10327 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10328 return SizePtrToInt;
10329}
10330
10333 std::string VarName) {
10334 llvm::Constant *MaptypesArrayInit =
10335 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10336 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10337 M, MaptypesArrayInit->getType(),
10338 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10339 VarName);
10340 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10341 return MaptypesArrayGlobal;
10342}
10343
10345 InsertPointTy AllocaIP,
10346 unsigned NumOperands,
10347 struct MapperAllocas &MapperAllocas) {
10348 if (!updateToLocation(Loc))
10349 return;
10350
10351 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10352 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10353 Builder.restoreIP(AllocaIP);
10354 AllocaInst *ArgsBase = Builder.CreateAlloca(
10355 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10356 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10357 ".offload_ptrs");
10358 AllocaInst *ArgSizes = Builder.CreateAlloca(
10359 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10361 MapperAllocas.ArgsBase = ArgsBase;
10362 MapperAllocas.Args = Args;
10363 MapperAllocas.ArgSizes = ArgSizes;
10364}
10365
10367 Function *MapperFunc, Value *SrcLocInfo,
10368 Value *MaptypesArg, Value *MapnamesArg,
10370 int64_t DeviceID, unsigned NumOperands) {
10371 if (!updateToLocation(Loc))
10372 return;
10373
10374 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10375 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10376 Value *ArgsBaseGEP =
10377 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10378 {Builder.getInt32(0), Builder.getInt32(0)});
10379 Value *ArgsGEP =
10380 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10381 {Builder.getInt32(0), Builder.getInt32(0)});
10382 Value *ArgSizesGEP =
10383 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10384 {Builder.getInt32(0), Builder.getInt32(0)});
10385 Value *NullPtr =
10386 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10387 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10388 Builder.getInt32(NumOperands),
10389 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10390 MaptypesArg, MapnamesArg, NullPtr});
10391}
10392
10394 TargetDataRTArgs &RTArgs,
10395 TargetDataInfo &Info,
10396 bool ForEndCall) {
10397 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10398 "expected region end call to runtime only when end call is separate");
10399 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10400 auto VoidPtrTy = UnqualPtrTy;
10401 auto VoidPtrPtrTy = UnqualPtrTy;
10402 auto Int64Ty = Type::getInt64Ty(M.getContext());
10403 auto Int64PtrTy = UnqualPtrTy;
10404
10405 if (!Info.NumberOfPtrs) {
10406 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10407 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10408 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10409 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10410 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10411 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10412 return;
10413 }
10414
10415 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10416 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10417 Info.RTArgs.BasePointersArray,
10418 /*Idx0=*/0, /*Idx1=*/0);
10419 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10420 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10421 /*Idx0=*/0,
10422 /*Idx1=*/0);
10423 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10424 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10425 /*Idx0=*/0, /*Idx1=*/0);
10426 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10427 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10428 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10429 : Info.RTArgs.MapTypesArray,
10430 /*Idx0=*/0,
10431 /*Idx1=*/0);
10432
10433 // Only emit the mapper information arrays if debug information is
10434 // requested.
10435 if (!Info.EmitDebug)
10436 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10437 else
10438 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10439 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10440 /*Idx0=*/0,
10441 /*Idx1=*/0);
10442 // If there is no user-defined mapper, set the mapper array to nullptr to
10443 // avoid an unnecessary data privatization
10444 if (!Info.HasMapper)
10445 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10446 else
10447 RTArgs.MappersArray =
10448 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10449}
10450
10452 InsertPointTy CodeGenIP,
10453 MapInfosTy &CombinedInfo,
10454 TargetDataInfo &Info) {
10456 CombinedInfo.NonContigInfo;
10457
10458 // Build an array of struct descriptor_dim and then assign it to
10459 // offload_args.
10460 //
10461 // struct descriptor_dim {
10462 // uint64_t offset;
10463 // uint64_t count;
10464 // uint64_t stride
10465 // };
10466 Type *Int64Ty = Builder.getInt64Ty();
10468 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10469 "struct.descriptor_dim");
10470
10471 enum { OffsetFD = 0, CountFD, StrideFD };
10472 // We need two index variable here since the size of "Dims" is the same as
10473 // the size of Components, however, the size of offset, count, and stride is
10474 // equal to the size of base declaration that is non-contiguous.
10475 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10476 // Skip emitting ir if dimension size is 1 since it cannot be
10477 // non-contiguous.
10478 if (NonContigInfo.Dims[I] == 1)
10479 continue;
10480 Builder.restoreIP(AllocaIP);
10481 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10482 AllocaInst *DimsAddr =
10483 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10484 Builder.restoreIP(CodeGenIP);
10485 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10486 unsigned RevIdx = EE - II - 1;
10487 Value *DimsLVal = Builder.CreateInBoundsGEP(
10488 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10489 // Offset
10490 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10491 Builder.CreateAlignedStore(
10492 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10493 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10494 // Count
10495 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10496 Builder.CreateAlignedStore(
10497 NonContigInfo.Counts[L][RevIdx], CountLVal,
10498 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10499 // Stride
10500 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10501 Builder.CreateAlignedStore(
10502 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10503 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10504 }
10505 // args[I] = &dims
10506 Builder.restoreIP(CodeGenIP);
10507 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10508 DimsAddr, Builder.getPtrTy());
10509 Value *P = Builder.CreateConstInBoundsGEP2_32(
10510 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10511 Info.RTArgs.PointersArray, 0, I);
10512 Builder.CreateAlignedStore(
10513 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10514 ++L;
10515 }
10516}
10517
10518void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10519 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10520 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10521 BasicBlock *ExitBB, bool IsInit) {
10522 StringRef Prefix = IsInit ? ".init" : ".del";
10523
10524 // Evaluate if this is an array section.
10526 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10527 Value *IsArray =
10528 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10529 Value *DeleteBit = Builder.CreateAnd(
10530 MapType,
10531 Builder.getInt64(
10532 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10533 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10534 Value *DeleteCond;
10535 Value *Cond;
10536 if (IsInit) {
10537 // base != begin?
10538 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10539 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10540 DeleteCond = Builder.CreateIsNull(
10541 DeleteBit,
10542 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10543 } else {
10544 Cond = IsArray;
10545 DeleteCond = Builder.CreateIsNotNull(
10546 DeleteBit,
10547 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10548 }
10549 Cond = Builder.CreateAnd(Cond, DeleteCond);
10550 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10551
10552 emitBlock(BodyBB, MapperFn);
10553 // Get the array size by multiplying element size and element number (i.e., \p
10554 // Size).
10555 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10556 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10557 // memory allocation/deletion purpose only.
10558 Value *MapTypeArg = Builder.CreateAnd(
10559 MapType,
10560 Builder.getInt64(
10561 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10562 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10563 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10564 MapTypeArg = Builder.CreateOr(
10565 MapTypeArg,
10566 Builder.getInt64(
10567 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10568 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10569
10570 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10571 // data structure.
10572 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10573 ArraySize, MapTypeArg, MapName};
10575 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10576 OffloadingArgs);
10577}
10578
10581 llvm::Value *BeginArg)>
10582 GenMapInfoCB,
10583 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10584 bool PreserveMemberOfFlags, bool PropagatePresentToPointee) {
10585 SmallVector<Type *> Params;
10586 Params.emplace_back(Builder.getPtrTy());
10587 Params.emplace_back(Builder.getPtrTy());
10588 Params.emplace_back(Builder.getPtrTy());
10589 Params.emplace_back(Builder.getInt64Ty());
10590 Params.emplace_back(Builder.getInt64Ty());
10591 Params.emplace_back(Builder.getPtrTy());
10592
10593 auto *FnTy =
10594 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10595
10596 SmallString<64> TyStr;
10597 raw_svector_ostream Out(TyStr);
10598 Function *MapperFn =
10600 MapperFn->addFnAttr(Attribute::NoInline);
10601 MapperFn->addFnAttr(Attribute::NoUnwind);
10602 MapperFn->addParamAttr(0, Attribute::NoUndef);
10603 MapperFn->addParamAttr(1, Attribute::NoUndef);
10604 MapperFn->addParamAttr(2, Attribute::NoUndef);
10605 MapperFn->addParamAttr(3, Attribute::NoUndef);
10606 MapperFn->addParamAttr(4, Attribute::NoUndef);
10607 MapperFn->addParamAttr(5, Attribute::NoUndef);
10608
10609 // Start the mapper function code generation.
10610 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10612 Builder.SetInsertPoint(EntryBB);
10613 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
10614
10615 Value *MapperHandle = MapperFn->getArg(0);
10616 Value *BaseIn = MapperFn->getArg(1);
10617 Value *BeginIn = MapperFn->getArg(2);
10618 Value *Size = MapperFn->getArg(3);
10619 Value *MapType = MapperFn->getArg(4);
10620 Value *MapName = MapperFn->getArg(5);
10621
10622 // Compute the starting and end addresses of array elements.
10623 // Prepare common arguments for array initiation and deletion.
10624 // Convert the size in bytes into the number of array elements.
10625 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10626 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10627 Value *PtrBegin = BeginIn;
10628 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10629
10630 // Emit array initiation if this is an array section and \p MapType indicates
10631 // that memory allocation is required.
10632 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10633 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10634 MapType, MapName, ElementSize, HeadBB,
10635 /*IsInit=*/true);
10636
10637 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10638
10639 // Emit the loop header block.
10640 emitBlock(HeadBB, MapperFn);
10641 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10642 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10643 // Evaluate whether the initial condition is satisfied.
10644 Value *IsEmpty =
10645 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10646 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10647
10648 // Emit the loop body block.
10649 emitBlock(BodyBB, MapperFn);
10650 BasicBlock *LastBB = BodyBB;
10651 PHINode *PtrPHI =
10652 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10653 PtrPHI->addIncoming(PtrBegin, HeadBB);
10654
10655 // Get map clause information. Fill up the arrays with all mapped variables.
10656 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10657 if (!Info)
10658 return Info.takeError();
10659
10660 // Call the runtime API __tgt_mapper_num_components to get the number of
10661 // pre-existing components.
10662 Value *OffloadingArgs[] = {MapperHandle};
10663 Value *PreviousSize = createRuntimeFunctionCall(
10664 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10665 OffloadingArgs);
10666 Value *ShiftedPreviousSize =
10667 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10668
10669 // Fill up the runtime mapper handle for all components.
10670 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10671 Value *CurBaseArg = Info->BasePointers[I];
10672 Value *CurBeginArg = Info->Pointers[I];
10673 Value *CurSizeArg = Info->Sizes[I];
10674 Value *CurNameArg = Info->Names.size()
10675 ? Info->Names[I]
10676 : Constant::getNullValue(Builder.getPtrTy());
10677
10678 Value *OriMapType = Builder.getInt64(
10679 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10680 Info->Types[I]));
10681 auto RawType =
10682 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10683 Info->Types[I]);
10684 constexpr uint64_t MemberOfMask =
10685 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10686 constexpr uint64_t AttachBit =
10687 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10688 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10689
10690 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10691 // current array element (N = __tgt_mapper_num_components() at loop body
10692 // start).
10693 //
10694 // Example 1:
10695 // struct S { int x; int *p; };
10696 //
10697 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10698 // use: S arr[2]; ... map(arr)
10699 // entries per element:
10700 //
10701 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10702 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10703 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10704 //
10705 // Example 2:
10706 // struct S1 { int x; int y; };
10707 // struct S2 { int z; S1 *s1p; };
10708 //
10709 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10710 // s2.s1p->y)
10711 // use: S2 arr[2]; ... map(arr)
10712 // entries per element:
10713 //
10714 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10715 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10716 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10717 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10718 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10719 //
10720 // x/y carry inner MEMBER_OF(2)
10721 // which is shifted by N to become MEMBER_OF(N+2).
10722 //
10723 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10724 // the combined ALLOC entry for the s1p->x..y block, and the individual
10725 // x/y entries that are MEMBER_OF that block, all describe storage
10726 // reached through the attach ptr arr[i].s1p.
10727 //
10728 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10729 // linking them to the parent struct:
10730 //
10731 // * (*) Entries with HasAttachPtr: they represent pointee data that
10732 // occupies a different storage block than the struct being mapped, so
10733 // they are not a member of it. They may still be MEMBER_OF an entry
10734 // within that pointee block, in which case those pre-existing bits are
10735 // shifted -- see (***).
10736 // * (**) ATTACH entries: they are not a member of anything — they just
10737 // link a ptr to its ptee.
10738 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10739 // its pre-shaped entries already carry their final MEMBER_OF bits.
10740 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10741 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10742 // it.
10743 //
10744 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10745 // s1p->x/y entries above), those bits are still shifted by N.
10746 Value *MemberMapType;
10747 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10748 Info->HasAttachPtr[I]) {
10749 if (RawType & MemberOfMask)
10750 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10751 else
10752 MemberMapType = OriMapType;
10753 } else {
10754 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10755 }
10756
10757 // Combine the map type inherited from user-defined mapper with that
10758 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10759 // bits of the \a MapType, which is the input argument of the mapper
10760 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10761 // bits of MemberMapType.
10762 // [OpenMP 5.0], 1.2.6. map-type decay.
10763 // | alloc | to | from | tofrom | release | delete
10764 // ----------------------------------------------------------
10765 // alloc | alloc | alloc | alloc | alloc | release | delete
10766 // to | alloc | to | alloc | to | release | delete
10767 // from | alloc | alloc | from | from | release | delete
10768 // tofrom | alloc | to | from | tofrom | release | delete
10769 Value *LeftToFrom = Builder.CreateAnd(
10770 MapType,
10771 Builder.getInt64(
10772 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10773 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10774 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10775 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10776 BasicBlock *AllocElseBB =
10777 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10778 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10779 BasicBlock *ToElseBB =
10780 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10781 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10782 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10783 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10784 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10785 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10786 emitBlock(AllocBB, MapperFn);
10787 Value *AllocMapType = Builder.CreateAnd(
10788 MemberMapType,
10789 Builder.getInt64(
10790 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10791 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10792 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10793 Builder.CreateBr(EndBB);
10794 emitBlock(AllocElseBB, MapperFn);
10795 Value *IsTo = Builder.CreateICmpEQ(
10796 LeftToFrom,
10797 Builder.getInt64(
10798 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10799 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10800 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10801 // In case of to, clear OMP_MAP_FROM.
10802 emitBlock(ToBB, MapperFn);
10803 Value *ToMapType = Builder.CreateAnd(
10804 MemberMapType,
10805 Builder.getInt64(
10806 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10807 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10808 Builder.CreateBr(EndBB);
10809 emitBlock(ToElseBB, MapperFn);
10810 Value *IsFrom = Builder.CreateICmpEQ(
10811 LeftToFrom,
10812 Builder.getInt64(
10813 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10814 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10815 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10816 // In case of from, clear OMP_MAP_TO.
10817 emitBlock(FromBB, MapperFn);
10818 Value *FromMapType = Builder.CreateAnd(
10819 MemberMapType,
10820 Builder.getInt64(
10821 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10822 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10823 // In case of tofrom, do nothing.
10824 emitBlock(EndBB, MapperFn);
10825 LastBB = EndBB;
10826 PHINode *CurMapType =
10827 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10828 CurMapType->addIncoming(AllocMapType, AllocBB);
10829 CurMapType->addIncoming(ToMapType, ToBB);
10830 CurMapType->addIncoming(FromMapType, FromBB);
10831 CurMapType->addIncoming(MemberMapType, ToElseBB);
10832
10833 // Propagate map-type-modifying bits from the outer map clause to each map
10834 // inserted by the mapper.
10835 //
10836 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10837 // list item from the map clause and to apply the clauses specified in the
10838 // declared mapper to the construct on which the map clause appears...
10839 // If any modifier with the map-type-modifying property appears in the map
10840 // clause then the effect is as if that modifier appears in each map clause
10841 // specified in the declared mapper.
10842 //
10843 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10844 //
10845 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
10846 //
10847 // PRESENT is propagated only to entries that have an attach ptr
10848 // (HasAttachPtr): the pointee data, which occupies a different storage
10849 // block than the struct being mapped and so is not covered by the
10850 // present-check on the struct's own storage. A present modifier on the
10851 // outer clause must still require that pointee to be present on the device.
10852 //
10853 // This is gated on \p PropagatePresentToPointee (set by callers only for
10854 // OpenMP >= 6.0). Before 6.0 the present modifier is treated as not
10855 // applying to the pointee: the spec committee confirmed the divergence
10856 // between the present "motion" modifier (to/from) and the present map-type
10857 // modifier (map) was unintentional, to be fixed as an OpenMP 6.0 erratum,
10858 // so for 5.2 present is ignored for the pointee for both map and to/from.
10859 //
10860 // TODO: PRESENT should also be propagated to the struct's own members
10861 // (e.g. the s.x, s.y of map(present, mapper(id): s)) so that an absent
10862 // member triggers the present-check. We cannot do that yet: while pointer
10863 // members are mapped with PTR_AND_OBJ, a single combined entry allocates
10864 // the whole struct (including the pointer's storage), so propagating
10865 // PRESENT to it would wrongly require the pointer's pointee to be present.
10866 // Enable member propagation once Clang stops emitting PTR_AND_OBJ and uses
10867 // attach-style maps throughout.
10868 uint64_t ModifierBits =
10869 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10870 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
10871 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
10872 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
10873 if (PropagatePresentToPointee && Info->HasAttachPtr[I])
10874 ModifierBits |=
10875 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10876 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10877 Value *ImportedModifierBits =
10878 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
10879 Value *CurMapTypeWithModifiers = Builder.CreateOr(
10880 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
10881
10882 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
10883 // reserved for the attach(always) map-type modifier, and other modifier
10884 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
10885 Value *FinalMapType =
10886 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
10887
10888 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
10889 CurSizeArg, FinalMapType, CurNameArg};
10890
10891 auto ChildMapperFn = CustomMapperCB(I);
10892 if (!ChildMapperFn)
10893 return ChildMapperFn.takeError();
10894 if (*ChildMapperFn) {
10895 // Call the corresponding mapper function.
10896 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
10897 ->setDoesNotThrow();
10898 } else {
10899 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10900 // data structure.
10902 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10903 OffloadingArgs);
10904 }
10905 }
10906
10907 // Update the pointer to point to the next element that needs to be mapped,
10908 // and check whether we have mapped all elements.
10909 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
10910 "omp.arraymap.next");
10911 PtrPHI->addIncoming(PtrNext, LastBB);
10912 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
10913 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
10914 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
10915
10916 emitBlock(ExitBB, MapperFn);
10917 // Emit array deletion if this is an array section and \p MapType indicates
10918 // that deletion is required.
10919 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10920 MapType, MapName, ElementSize, DoneBB,
10921 /*IsInit=*/false);
10922
10923 // Emit the function exit block.
10924 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
10925
10926 Builder.CreateRetVoid();
10927 return MapperFn;
10928}
10929
10931 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
10932 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
10933 bool IsNonContiguous,
10934 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10935
10936 // Reset the array information.
10937 Info.clearArrayInfo();
10938 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
10939
10940 if (Info.NumberOfPtrs == 0)
10941 return Error::success();
10942
10943 Builder.restoreIP(AllocaIP);
10944 // Detect if we have any capture size requiring runtime evaluation of the
10945 // size so that a constant array could be eventually used.
10946 ArrayType *PointerArrayType =
10947 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
10948
10949 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
10950 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
10951
10952 Info.RTArgs.PointersArray = Builder.CreateAlloca(
10953 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
10954 AllocaInst *MappersArray = Builder.CreateAlloca(
10955 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
10956 Info.RTArgs.MappersArray = MappersArray;
10957
10958 // If we don't have any VLA types or other types that require runtime
10959 // evaluation, we can use a constant array for the map sizes, otherwise we
10960 // need to fill up the arrays as we do for the pointers.
10961 Type *Int64Ty = Builder.getInt64Ty();
10962 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
10963 ConstantInt::get(Int64Ty, 0));
10964 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
10965 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
10966 bool IsNonContigEntry =
10967 IsNonContiguous &&
10968 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10969 CombinedInfo.Types[I] &
10970 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
10971 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
10972 // descriptor_dim records), not the byte size.
10973 if (IsNonContigEntry) {
10974 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
10975 "Index must be in-bounds for NON_CONTIG Dims array");
10976 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
10977 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
10978 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
10979 continue;
10980 }
10981 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
10982 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
10983 ConstSizes[I] = CI;
10984 continue;
10985 }
10986 }
10987 RuntimeSizes.set(I);
10988 }
10989
10990 if (RuntimeSizes.all()) {
10991 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
10992 Info.RTArgs.SizesArray = Builder.CreateAlloca(
10993 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
10994 restoreIPandDebugLoc(Builder, CodeGenIP);
10995 } else {
10996 auto *SizesArrayInit = ConstantArray::get(
10997 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
10998 std::string Name = createPlatformSpecificName({"offload_sizes"});
10999 auto *SizesArrayGbl =
11000 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
11001 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
11002 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
11003
11004 if (!RuntimeSizes.any()) {
11005 Info.RTArgs.SizesArray = SizesArrayGbl;
11006 } else {
11007 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11008 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
11009 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11010 AllocaInst *Buffer = Builder.CreateAlloca(
11011 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11012 Buffer->setAlignment(OffloadSizeAlign);
11013 restoreIPandDebugLoc(Builder, CodeGenIP);
11014 Builder.CreateMemCpy(
11015 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11016 SizesArrayGbl, OffloadSizeAlign,
11017 Builder.getIntN(
11018 IndexSize,
11019 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11020
11021 Info.RTArgs.SizesArray = Buffer;
11022 }
11023 restoreIPandDebugLoc(Builder, CodeGenIP);
11024 }
11025
11026 // The map types are always constant so we don't need to generate code to
11027 // fill arrays. Instead, we create an array constant.
11029 for (auto mapFlag : CombinedInfo.Types)
11030 Mapping.push_back(
11031 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11032 mapFlag));
11033 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11034 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11035 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11036
11037 // The information types are only built if provided.
11038 if (!CombinedInfo.Names.empty()) {
11039 auto *MapNamesArrayGbl = createOffloadMapnames(
11040 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11041 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11042 Info.EmitDebug = true;
11043 } else {
11044 Info.RTArgs.MapNamesArray =
11046 Info.EmitDebug = false;
11047 }
11048
11049 // If there's a present map type modifier, it must not be applied to the end
11050 // of a region, so generate a separate map type array in that case.
11051 if (Info.separateBeginEndCalls()) {
11052 bool EndMapTypesDiffer = false;
11053 for (uint64_t &Type : Mapping) {
11054 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11055 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11056 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11057 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11058 EndMapTypesDiffer = true;
11059 }
11060 }
11061 if (EndMapTypesDiffer) {
11062 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11063 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11064 }
11065 }
11066
11067 PointerType *PtrTy = Builder.getPtrTy();
11068 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11069 Value *BPVal = CombinedInfo.BasePointers[I];
11070 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11071 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11072 0, I);
11073 Builder.CreateAlignedStore(BPVal, BP,
11074 M.getDataLayout().getPrefTypeAlign(PtrTy));
11075
11076 if (Info.requiresDevicePointerInfo()) {
11077 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11078 CodeGenIP = Builder.saveIP();
11079 Builder.restoreIP(AllocaIP);
11080 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11081 restoreIPandDebugLoc(Builder, CodeGenIP);
11082 if (DeviceAddrCB)
11083 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11084 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11085 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11086 if (DeviceAddrCB)
11087 DeviceAddrCB(I, BP);
11088 }
11089 }
11090
11091 Value *PVal = CombinedInfo.Pointers[I];
11092 Value *P = Builder.CreateConstInBoundsGEP2_32(
11093 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11094 I);
11095 // TODO: Check alignment correct.
11096 Builder.CreateAlignedStore(PVal, P,
11097 M.getDataLayout().getPrefTypeAlign(PtrTy));
11098
11099 if (RuntimeSizes.test(I)) {
11100 Value *S = Builder.CreateConstInBoundsGEP2_32(
11101 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11102 /*Idx0=*/0,
11103 /*Idx1=*/I);
11104 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11105 Int64Ty,
11106 /*isSigned=*/true),
11107 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11108 }
11109 // Fill up the mapper array.
11110 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11111 Value *MFunc = ConstantPointerNull::get(PtrTy);
11112
11113 auto CustomMFunc = CustomMapperCB(I);
11114 if (!CustomMFunc)
11115 return CustomMFunc.takeError();
11116 if (*CustomMFunc)
11117 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11118
11119 Value *MAddr = Builder.CreateInBoundsGEP(
11120 PointerArrayType, MappersArray,
11121 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11122 Builder.CreateAlignedStore(
11123 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11124 }
11125
11126 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11127 Info.NumberOfPtrs == 0)
11128 return Error::success();
11129 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11130 return Error::success();
11131}
11132
11134 BasicBlock *CurBB = Builder.GetInsertBlock();
11135
11136 if (!CurBB || CurBB->hasTerminator()) {
11137 // If there is no insert point or the previous block is already
11138 // terminated, don't touch it.
11139 } else {
11140 // Otherwise, create a fall-through branch.
11141 Builder.CreateBr(Target);
11142 }
11143
11144 Builder.ClearInsertionPoint();
11145}
11146
11148 bool IsFinished) {
11149 BasicBlock *CurBB = Builder.GetInsertBlock();
11150
11151 // Fall out of the current block (if necessary).
11152 emitBranch(BB);
11153
11154 if (IsFinished && BB->use_empty()) {
11155 BB->eraseFromParent();
11156 return;
11157 }
11158
11159 // Place the block after the current block, if possible, or else at
11160 // the end of the function.
11161 if (CurBB && CurBB->getParent())
11162 CurFn->insert(std::next(CurBB->getIterator()), BB);
11163 else
11164 CurFn->insert(CurFn->end(), BB);
11165 Builder.SetInsertPoint(BB);
11166}
11167
11169 BodyGenCallbackTy ElseGen,
11170 InsertPointTy AllocaIP,
11171 ArrayRef<BasicBlock *> DeallocBlocks) {
11172 // If the condition constant folds and can be elided, try to avoid emitting
11173 // the condition and the dead arm of the if/else.
11174 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11175 auto CondConstant = CI->getSExtValue();
11176 if (CondConstant)
11177 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11178
11179 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11180 }
11181
11182 Function *CurFn = Builder.GetInsertBlock()->getParent();
11183
11184 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11185 // emit the conditional branch.
11186 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11187 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11188 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11189 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11190 // Emit the 'then' code.
11191 emitBlock(ThenBlock, CurFn);
11192 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11193 return Err;
11194 emitBranch(ContBlock);
11195 // Emit the 'else' code if present.
11196 // There is no need to emit line number for unconditional branch.
11197 emitBlock(ElseBlock, CurFn);
11198 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11199 return Err;
11200 // There is no need to emit line number for unconditional branch.
11201 emitBranch(ContBlock);
11202 // Emit the continuation block for code after the if.
11203 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11204 return Error::success();
11205}
11206
11207bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11208 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11211 "Unexpected Atomic Ordering.");
11212
11213 bool Flush = false;
11215
11216 switch (AK) {
11217 case Read:
11220 FlushAO = AtomicOrdering::Acquire;
11221 Flush = true;
11222 }
11223 break;
11224 case Write:
11225 case Compare:
11226 case Update:
11229 FlushAO = AtomicOrdering::Release;
11230 Flush = true;
11231 }
11232 break;
11233 case Capture:
11234 switch (AO) {
11236 FlushAO = AtomicOrdering::Acquire;
11237 Flush = true;
11238 break;
11240 FlushAO = AtomicOrdering::Release;
11241 Flush = true;
11242 break;
11246 Flush = true;
11247 break;
11248 default:
11249 // do nothing - leave silently.
11250 break;
11251 }
11252 }
11253
11254 if (Flush) {
11255 // Currently Flush RT call still doesn't take memory_ordering, so for when
11256 // that happens, this tries to do the resolution of which atomic ordering
11257 // to use with but issue the flush call
11258 // TODO: pass `FlushAO` after memory ordering support is added
11259 (void)FlushAO;
11260 emitFlush(Loc);
11261 }
11262
11263 // for AO == AtomicOrdering::Monotonic and all other case combinations
11264 // do nothing
11265 return Flush;
11266}
11267
11271 AtomicOrdering AO, InsertPointTy AllocaIP) {
11272 if (!updateToLocation(Loc))
11273 return Loc.IP;
11274
11275 assert(X.Var->getType()->isPointerTy() &&
11276 "OMP Atomic expects a pointer to target memory");
11277 Type *XElemTy = X.ElemTy;
11278 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11279 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11280 "OMP atomic read expected a scalar type");
11281
11282 Value *XRead = nullptr;
11283
11284 if (XElemTy->isIntegerTy()) {
11285 LoadInst *XLD =
11286 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11287 XLD->setAtomic(AO);
11288 XRead = cast<Value>(XLD);
11289 } else if (XElemTy->isStructTy()) {
11290 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11291 // target does not support `atomicrmw` of the size of the struct
11292 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11293 OldVal->setAtomic(AO);
11294 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11295 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11296 OpenMPIRBuilder::AtomicInfo atomicInfo(
11297 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11298 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11299 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11300 XRead = AtomicLoadRes.first;
11301 OldVal->eraseFromParent();
11302 } else {
11303 // We need to perform atomic op as integer
11304 IntegerType *IntCastTy =
11305 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11306 LoadInst *XLoad =
11307 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11308 XLoad->setAtomic(AO);
11309 if (XElemTy->isFloatingPointTy()) {
11310 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11311 } else {
11312 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11313 }
11314 }
11315 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11316 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11317 return Builder.saveIP();
11318}
11319
11322 AtomicOpValue &X, Value *Expr,
11323 AtomicOrdering AO, InsertPointTy AllocaIP) {
11324 if (!updateToLocation(Loc))
11325 return Loc.IP;
11326
11327 assert(X.Var->getType()->isPointerTy() &&
11328 "OMP Atomic expects a pointer to target memory");
11329 Type *XElemTy = X.ElemTy;
11330 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11331 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11332 "OMP atomic write expected a scalar type");
11333
11334 if (XElemTy->isIntegerTy()) {
11335 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11336 XSt->setAtomic(AO);
11337 } else if (XElemTy->isStructTy()) {
11338 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11339 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11340 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11341 OpenMPIRBuilder::AtomicInfo atomicInfo(
11342 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11343 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11344 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11345 OldVal->eraseFromParent();
11346 } else {
11347 // We need to bitcast and perform atomic op as integers
11348 IntegerType *IntCastTy =
11349 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11350 Value *ExprCast =
11351 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11352 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11353 XSt->setAtomic(AO);
11354 }
11355
11356 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11357 return Builder.saveIP();
11358}
11359
11362 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11363 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11364 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11365 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11366 if (!updateToLocation(Loc))
11367 return Loc.IP;
11368
11369 LLVM_DEBUG({
11370 Type *XTy = X.Var->getType();
11371 assert(XTy->isPointerTy() &&
11372 "OMP Atomic expects a pointer to target memory");
11373 Type *XElemTy = X.ElemTy;
11374 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11375 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11376 "OMP atomic update expected a scalar or struct type");
11377 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11378 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&
11379 "OpenMP atomic does not support LT or GT operations");
11380 });
11381
11382 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11383 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11384 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11385 if (!AtomicResult)
11386 return AtomicResult.takeError();
11387 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11388 return Builder.saveIP();
11389}
11390
11391// FIXME: Duplicating AtomicExpand
11392Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11393 AtomicRMWInst::BinOp RMWOp) {
11394 switch (RMWOp) {
11395 case AtomicRMWInst::Add:
11396 return Builder.CreateAdd(Src1, Src2);
11397 case AtomicRMWInst::Sub:
11398 return Builder.CreateSub(Src1, Src2);
11399 case AtomicRMWInst::And:
11400 return Builder.CreateAnd(Src1, Src2);
11402 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11403 case AtomicRMWInst::Or:
11404 return Builder.CreateOr(Src1, Src2);
11405 case AtomicRMWInst::Xor:
11406 return Builder.CreateXor(Src1, Src2);
11411 case AtomicRMWInst::Max:
11412 case AtomicRMWInst::Min:
11425 llvm_unreachable("Unsupported atomic update operation");
11426 }
11427 llvm_unreachable("Unsupported atomic update operation");
11428}
11429
11431 // Loads cannot use Release or AcquireRelease ordering. This load is
11432 // just the initial value for the cmpxchg loop; the cmpxchg itself
11433 // retains the original ordering.
11434 AtomicOrdering LoadAO = AO;
11435
11436 if (AO == AtomicOrdering::Release) {
11438 } else if (AO == AtomicOrdering::AcquireRelease) {
11439 LoadAO = AtomicOrdering::Acquire;
11440 }
11441
11442 return LoadAO;
11443}
11444
11445Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11446 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11448 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11449 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11450 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11451 bool emitRMWOp = false;
11452 switch (RMWOp) {
11453 case AtomicRMWInst::Add:
11454 case AtomicRMWInst::And:
11456 case AtomicRMWInst::Or:
11457 case AtomicRMWInst::Xor:
11459 emitRMWOp = XElemTy;
11460 break;
11461 case AtomicRMWInst::Sub:
11462 emitRMWOp = (IsXBinopExpr && XElemTy);
11463 break;
11464 default:
11465 emitRMWOp = false;
11466 }
11467 emitRMWOp &= XElemTy->isIntegerTy();
11468
11469 std::pair<Value *, Value *> Res;
11470 if (emitRMWOp) {
11471 AtomicRMWInst *RMWInst =
11472 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11473 if (T.isAMDGPU()) {
11474 if (IsIgnoreDenormalMode)
11475 RMWInst->setMetadata("amdgpu.ignore.denormal.mode",
11476 llvm::MDNode::get(Builder.getContext(), {}));
11477 if (!IsFineGrainedMemory)
11478 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11479 llvm::MDNode::get(Builder.getContext(), {}));
11480 if (!IsRemoteMemory)
11481 RMWInst->setMetadata("amdgpu.no.remote.memory",
11482 llvm::MDNode::get(Builder.getContext(), {}));
11483 }
11484 Res.first = RMWInst;
11485 // not needed except in case of postfix captures. Generate anyway for
11486 // consistency with the else part. Will be removed with any DCE pass.
11487 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11488 if (RMWOp == AtomicRMWInst::Xchg)
11489 Res.second = Res.first;
11490 else
11491 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11492 } else if (XElemTy->isStructTy()) {
11493 LoadInst *OldVal =
11494 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11496 OldVal->setAtomic(LoadAO);
11497 const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
11498 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11499
11500 OpenMPIRBuilder::AtomicInfo atomicInfo(
11501 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11502 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11503 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11504 BasicBlock *CurBB = Builder.GetInsertBlock();
11505 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11506 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11507 BasicBlock *ExitBB =
11508 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11509 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11510 X->getName() + ".atomic.cont");
11511 ContBB->getTerminator()->eraseFromParent();
11512 Builder.restoreIP(AllocaIP);
11513 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11514 NewAtomicAddr->setName(X->getName() + "x.new.val");
11515 Builder.SetInsertPoint(ContBB);
11516 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11517 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11518 Value *OldExprVal = PHI;
11519 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11520 if (!CBResult)
11521 return CBResult.takeError();
11522 Value *Upd = *CBResult;
11523 Builder.CreateStore(Upd, NewAtomicAddr);
11526 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11527 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11528 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11529 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11530 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11531 OldVal->eraseFromParent();
11532 Res.first = OldExprVal;
11533 Res.second = Upd;
11534
11535 if (UnreachableInst *ExitTI =
11537 CurBBTI->eraseFromParent();
11538 Builder.SetInsertPoint(ExitBB);
11539 } else {
11540 Builder.SetInsertPoint(ExitTI);
11541 }
11542 } else {
11543 IntegerType *IntCastTy =
11544 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11545 LoadInst *OldVal =
11546 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11548 OldVal->setAtomic(LoadAO);
11549 // CurBB
11550 // | /---\
11551 // ContBB |
11552 // | \---/
11553 // ExitBB
11554 BasicBlock *CurBB = Builder.GetInsertBlock();
11555 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11556 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11557 BasicBlock *ExitBB =
11558 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11559 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11560 X->getName() + ".atomic.cont");
11561 ContBB->getTerminator()->eraseFromParent();
11562 Builder.restoreIP(AllocaIP);
11563 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11564 NewAtomicAddr->setName(X->getName() + "x.new.val");
11565 Builder.SetInsertPoint(ContBB);
11566 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11567 PHI->addIncoming(OldVal, CurBB);
11568 bool IsIntTy = XElemTy->isIntegerTy();
11569 Value *OldExprVal = PHI;
11570 if (!IsIntTy) {
11571 if (XElemTy->isFloatingPointTy()) {
11572 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11573 X->getName() + ".atomic.fltCast");
11574 } else {
11575 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11576 X->getName() + ".atomic.ptrCast");
11577 }
11578 }
11579
11580 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11581 if (!CBResult)
11582 return CBResult.takeError();
11583 Value *Upd = *CBResult;
11584 Builder.CreateStore(Upd, NewAtomicAddr);
11585 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11588 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11589 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11590 Result->setVolatile(VolatileX);
11591 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11592 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11593 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11594 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11595
11596 Res.first = OldExprVal;
11597 Res.second = Upd;
11598
11599 // set Insertion point in exit block
11600 if (UnreachableInst *ExitTI =
11602 CurBBTI->eraseFromParent();
11603 Builder.SetInsertPoint(ExitBB);
11604 } else {
11605 Builder.SetInsertPoint(ExitTI);
11606 }
11607 }
11608
11609 return Res;
11610}
11611
11614 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11615 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11616 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11617 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11618 if (!updateToLocation(Loc))
11619 return Loc.IP;
11620
11621 LLVM_DEBUG({
11622 Type *XTy = X.Var->getType();
11623 assert(XTy->isPointerTy() &&
11624 "OMP Atomic expects a pointer to target memory");
11625 Type *XElemTy = X.ElemTy;
11626 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11627 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11628 "OMP atomic capture expected a scalar or struct type");
11629 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11630 "OpenMP atomic does not support LT or GT operations");
11631 });
11632
11633 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11634 // 'x' is simply atomically rewritten with 'expr'.
11635 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11636 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11637 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11638 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11639 if (!AtomicResult)
11640 return AtomicResult.takeError();
11641 Value *CapturedVal =
11642 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11643 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11644
11645 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11646 return Builder.saveIP();
11647}
11648
11652 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11653 bool IsFailOnly, bool IsWeak) {
11654
11656 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11657 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11658}
11659
11663 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11664 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11665
11666 if (!updateToLocation(Loc))
11667 return Loc.IP;
11668
11669 assert(X.Var->getType()->isPointerTy() &&
11670 "OMP atomic expects a pointer to target memory");
11671 // compare capture
11672 if (V.Var) {
11673 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11674 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11675 }
11676
11677 bool IsInteger = E->getType()->isIntegerTy();
11678
11679 if (Op == OMPAtomicCompareOp::EQ) {
11680 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11681 // R.Var handling.
11682 Value *OldValue = nullptr;
11683 Value *SuccessOrFail = nullptr;
11684
11685 if (!IsInteger && HandleFPNegZero) {
11686 // IEEE 754 special cases for cmpxchg (which is bitwise):
11687 // 1. -0.0 == +0.0 but they have different bit patterns.
11688 // 2. NaN != NaN but identical NaN bit patterns would match.
11689 //
11690 // CurBB:
11691 // %e_int = bitcast E to intN
11692 // %d_int = bitcast D to intN
11693 // %x_curr = load atomic intN, X
11694 // %x_fp = bitcast %x_curr to FP
11695 // %e_is_nan = fcmp uno E, E
11696 // %x_is_nan = fcmp uno %x_fp, %x_fp
11697 // %either_nan = or %e_is_nan, %x_is_nan
11698 // br %either_nan, NaNBB, NotNaNBB
11699 // NaNBB: ; NaN == anything is always false
11700 // br ExitBB
11701 // NotNaNBB:
11702 // %x_is_zero = fcmp oeq %x_fp, 0.0
11703 // %e_is_zero = fcmp oeq E, 0.0
11704 // %both_zero = and %x_is_zero, %e_is_zero
11705 // br %both_zero, ZeroBB, NormalBB
11706 // ZeroBB: ; both ±0.0 → x = d
11707 // cmpxchg X, %x_curr, %d_int
11708 // br ExitBB
11709 // NormalBB: ; original path
11710 // cmpxchg X, %e_int, %d_int
11711 // br ExitBB
11712 // ExitBB:
11713 // phi merge
11714 IntegerType *IntCastTy =
11715 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11716 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11717 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11718
11719 // Load X atomically.
11720 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11721 X.Var->getName() + ".atomic.load");
11723 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11724
11725 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11726 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11727 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11728 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11729 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11730
11731 BasicBlock *CurBB = Builder.GetInsertBlock();
11732 Function *F = CurBB->getParent();
11733 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11734 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11735 BasicBlock *ExitBB =
11736 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11738 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11739 BasicBlock *NotNaNBB = BasicBlock::Create(
11740 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11742 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11743 BasicBlock *NormalBB = BasicBlock::Create(
11744 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11745
11746 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11747 CurBB->getTerminator()->eraseFromParent();
11748 Builder.SetInsertPoint(CurBB);
11749 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11750
11751 // NaNBB: NaN == anything is always false; skip cmpxchg.
11752 Builder.SetInsertPoint(NaNBB);
11753 Builder.CreateBr(ExitBB);
11754
11755 // NotNaNBB: check both X and E for ±0.0.
11756 Builder.SetInsertPoint(NotNaNBB);
11757 Value *XIsZero =
11758 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11759 X.Var->getName() + ".atomic.xiszero");
11760 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11761 "atomic.e.iszero");
11762 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11763 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11764
11765 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11766 Builder.SetInsertPoint(ZeroBB);
11767 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11768 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11769 ResZero->setWeak(IsWeak);
11770 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11771 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11772 Builder.CreateBr(ExitBB);
11773
11774 // NormalBB: original bitwise cmpxchg.
11775 Builder.SetInsertPoint(NormalBB);
11776 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11777 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11778 ResNormal->setWeak(IsWeak);
11779 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11780 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11781 Builder.CreateBr(ExitBB);
11782
11783 // ExitBB: merge results from NaN, Zero, and Normal paths.
11784 Builder.SetInsertPoint(ExitBB, ExitBB->begin());
11785 PHINode *OldIntPHI =
11786 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11787 OldIntPHI->addIncoming(XCurr, NaNBB);
11788 OldIntPHI->addIncoming(OldZero, ZeroBB);
11789 OldIntPHI->addIncoming(OldNormal, NormalBB);
11790 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11791 X.Var->getName() + ".atomic.ok");
11792 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11793 SuccessPHI->addIncoming(OkZero, ZeroBB);
11794 SuccessPHI->addIncoming(OkNormal, NormalBB);
11795
11796 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11797 CurBBTI->eraseFromParent();
11798 Builder.SetInsertPoint(ExitBB);
11799 } else {
11800 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11801 }
11802
11803 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11804 X.Var->getName() + ".atomic.old.fp");
11805 SuccessOrFail = SuccessPHI;
11806 } else {
11807 AtomicCmpXchgInst *Result = nullptr;
11808 if (!IsInteger) {
11809 IntegerType *IntCastTy =
11810 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11811 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11812 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11813 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11814 MaybeAlign(), AO, Failure);
11815 } else {
11816 Result =
11817 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11818 }
11819 Result->setWeak(IsWeak);
11820
11821 if (V.Var) {
11822 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11823 if (!IsInteger)
11824 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11825 assert(OldValue->getType() == V.ElemTy &&
11826 "OldValue and V must be of same type");
11827 if (IsPostfixUpdate) {
11828 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11829 } else {
11830 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11831 if (IsFailOnly) {
11832 BasicBlock *CurBB = Builder.GetInsertBlock();
11833 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11834 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11835 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11836 CurBBTI, X.Var->getName() + ".atomic.exit");
11837 BasicBlock *ContBB = CurBB->splitBasicBlock(
11838 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11839 ContBB->getTerminator()->eraseFromParent();
11840 CurBB->getTerminator()->eraseFromParent();
11841
11842 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11843
11844 Builder.SetInsertPoint(ContBB);
11845 Builder.CreateStore(OldValue, V.Var);
11846 Builder.CreateBr(ExitBB);
11847
11848 if (UnreachableInst *ExitTI =
11850 CurBBTI->eraseFromParent();
11851 Builder.SetInsertPoint(ExitBB);
11852 } else {
11853 Builder.SetInsertPoint(ExitTI);
11854 }
11855 } else {
11856 Value *CapturedValue =
11857 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11858 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11859 }
11860 }
11861 }
11862 // The comparison result has to be stored.
11863 if (R.Var) {
11864 assert(R.Var->getType()->isPointerTy() &&
11865 "r.var must be of pointer type");
11866 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11867
11868 Value *SuccessFailureVal =
11869 Builder.CreateExtractValue(Result, /*Idxs=*/1);
11870 Value *ResultCast =
11871 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
11872 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
11873 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11874 }
11875 }
11876
11877 // For the HandleFPNegZero path, handle V.Var and R.Var using the
11878 // pre-computed OldValue and SuccessOrFail.
11879 if (HandleFPNegZero && !IsInteger) {
11880 if (V.Var) {
11881 assert(OldValue->getType() == V.ElemTy &&
11882 "OldValue and V must be of same type");
11883 if (IsPostfixUpdate) {
11884 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11885 } else {
11886 if (IsFailOnly) {
11887 BasicBlock *CurBB = Builder.GetInsertBlock();
11888 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11889 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11890 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11891 CurBBTI, X.Var->getName() + ".atomic.exit");
11892 BasicBlock *ContBB = CurBB->splitBasicBlock(
11893 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11894 ContBB->getTerminator()->eraseFromParent();
11895 CurBB->getTerminator()->eraseFromParent();
11896
11897 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11898
11899 Builder.SetInsertPoint(ContBB);
11900 Builder.CreateStore(OldValue, V.Var);
11901 Builder.CreateBr(ExitBB);
11902
11903 if (UnreachableInst *ExitTI =
11905 CurBBTI->eraseFromParent();
11906 Builder.SetInsertPoint(ExitBB);
11907 } else {
11908 Builder.SetInsertPoint(ExitTI);
11909 }
11910 } else {
11911 Value *CapturedValue =
11912 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11913 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11914 }
11915 }
11916 }
11917 // The comparison result has to be stored.
11918 if (R.Var) {
11919 assert(R.Var->getType()->isPointerTy() &&
11920 "r.var must be of pointer type");
11921 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11922
11923 Value *ResultCast = R.IsSigned
11924 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
11925 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
11926 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11927 }
11928 }
11929 } else {
11930 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
11931 "Op should be either max or min at this point");
11932 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
11933
11934 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
11935 // Let's take max as example.
11936 // OpenMP form:
11937 // x = x > expr ? expr : x;
11938 // LLVM form:
11939 // *ptr = *ptr > val ? *ptr : val;
11940 // We need to transform to LLVM form.
11941 // x = x <= expr ? x : expr;
11943 if (IsXBinopExpr) {
11944 if (IsInteger) {
11945 if (X.IsSigned)
11946 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
11948 else
11949 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
11951 } else {
11952 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
11954 }
11955 } else {
11956 if (IsInteger) {
11957 if (X.IsSigned)
11958 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
11960 else
11961 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
11963 } else {
11964 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
11966 }
11967 }
11968
11969 AtomicRMWInst *OldValue =
11970 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
11971 if (V.Var) {
11972 Value *CapturedValue = nullptr;
11973 if (IsPostfixUpdate) {
11974 CapturedValue = OldValue;
11975 } else {
11976 CmpInst::Predicate Pred;
11977 switch (NewOp) {
11978 case AtomicRMWInst::Max:
11979 Pred = CmpInst::ICMP_SGT;
11980 break;
11982 Pred = CmpInst::ICMP_UGT;
11983 break;
11985 Pred = CmpInst::FCMP_OGT;
11986 break;
11987 case AtomicRMWInst::Min:
11988 Pred = CmpInst::ICMP_SLT;
11989 break;
11991 Pred = CmpInst::ICMP_ULT;
11992 break;
11994 Pred = CmpInst::FCMP_OLT;
11995 break;
11996 default:
11997 llvm_unreachable("unexpected comparison op");
11998 }
11999 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
12000 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
12001 }
12002 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12003 }
12004 }
12005
12006 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
12007
12008 return Builder.saveIP();
12009}
12010
12013 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12014 Value *NumTeamsUpper, Value *ThreadLimit,
12015 Value *IfExpr) {
12016 if (!updateToLocation(Loc))
12017 return InsertPointTy();
12018
12019 uint32_t SrcLocStrSize;
12020 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12021 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12022 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12023
12024 // Outer allocation basicblock is the entry block of the current function.
12025 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12026 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12027 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12028 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12029 }
12030
12031 // The current basic block is split into four basic blocks. After outlining,
12032 // they will be mapped as follows:
12033 // ```
12034 // def current_fn() {
12035 // current_basic_block:
12036 // br label %teams.exit
12037 // teams.exit:
12038 // ; instructions after teams
12039 // }
12040 //
12041 // def outlined_fn() {
12042 // teams.alloca:
12043 // br label %teams.body
12044 // teams.body:
12045 // ; instructions within teams body
12046 // }
12047 // ```
12048 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12049 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12050 BasicBlock *AllocaBB =
12051 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12052
12053 bool SubClausesPresent =
12054 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12055 // Push num_teams
12056 if (!Config.isTargetDevice() && SubClausesPresent) {
12057 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12058 "if lowerbound is non-null, then upperbound must also be non-null "
12059 "for bounds on num_teams");
12060
12061 if (NumTeamsUpper == nullptr)
12062 NumTeamsUpper = Builder.getInt32(0);
12063
12064 if (NumTeamsLower == nullptr)
12065 NumTeamsLower = NumTeamsUpper;
12066
12067 if (IfExpr) {
12068 assert(IfExpr->getType()->isIntegerTy() &&
12069 "argument to if clause must be an integer value");
12070
12071 // upper = ifexpr ? upper : 1
12072 if (IfExpr->getType() != Int1)
12073 IfExpr = Builder.CreateICmpNE(IfExpr,
12074 ConstantInt::get(IfExpr->getType(), 0));
12075 NumTeamsUpper = Builder.CreateSelect(
12076 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12077
12078 // lower = ifexpr ? lower : 1
12079 NumTeamsLower = Builder.CreateSelect(
12080 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12081 }
12082
12083 if (ThreadLimit == nullptr)
12084 ThreadLimit = Builder.getInt32(0);
12085
12086 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12087 // truncate or sign extend the passed values to match the int32 parameters.
12088 Value *NumTeamsLowerInt32 =
12089 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12090 Value *NumTeamsUpperInt32 =
12091 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12092 Value *ThreadLimitInt32 =
12093 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12094
12095 Value *ThreadNum = getOrCreateThreadID(Ident);
12096
12098 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12099 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12100 ThreadLimitInt32});
12101 }
12102 // Generate the body of teams.
12103 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12104 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12105 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12106 return Err;
12107
12108 auto OI = std::make_unique<OutlineInfo>();
12109 OI->EntryBB = AllocaBB;
12110 OI->ExitBB = ExitBB;
12111 OI->OuterAllocBB = &OuterAllocaBB;
12112
12113 // Insert fake values for global tid and bound tid.
12115 InsertPointTy OuterAllocaIP(&OuterAllocaBB, OuterAllocaBB.begin());
12116 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12117 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12118 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12119 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12120
12121 auto HostPostOutlineCB = [this, Ident,
12122 ToBeDeleted](Function &OutlinedFn) mutable {
12123 // The stale call instruction will be replaced with a new call instruction
12124 // for runtime call with the outlined function.
12125
12126 assert(OutlinedFn.hasOneUse() &&
12127 "there must be a single user for the outlined function");
12128 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12129 ToBeDeleted.push_back(StaleCI);
12130
12131 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12132 "Outlined function must have two or three arguments only");
12133
12134 bool HasShared = OutlinedFn.arg_size() == 3;
12135
12136 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12137 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12138 if (HasShared)
12139 OutlinedFn.getArg(2)->setName("data");
12140
12141 // Call to the runtime function for teams in the current function.
12142 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12143 "outlined function.");
12144 Builder.SetInsertPoint(StaleCI);
12145 SmallVector<Value *> Args = {
12146 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12147 if (HasShared)
12148 Args.push_back(StaleCI->getArgOperand(2));
12151 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12152 Args);
12153
12154 Builder.ClearInsertionPoint();
12155 for (Instruction *I : llvm::reverse(ToBeDeleted))
12156 I->eraseFromParent();
12157 };
12158
12159 if (!Config.isTargetDevice())
12160 OI->PostOutlineCB = HostPostOutlineCB;
12161
12162 addOutlineInfo(std::move(OI));
12163
12164 Builder.SetInsertPoint(ExitBB);
12165
12166 return Builder.saveIP();
12167}
12168
12170 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12171 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12172 if (!updateToLocation(Loc))
12173 return InsertPointTy();
12174
12175 BasicBlock *OuterAllocaBB = OuterAllocIP.getBlock();
12176
12177 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12178 BasicBlock *BodyBB =
12179 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12180 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12181 }
12182 BasicBlock *ExitBB =
12183 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12184 BasicBlock *BodyBB =
12185 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12186 BasicBlock *AllocaBB =
12187 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12188
12189 // Generate the body of distribute clause
12190 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12191 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12192 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12193 return Err;
12194
12195 // When using target we use different runtime functions which require a
12196 // callback.
12197 if (Config.isTargetDevice()) {
12198 auto OI = std::make_unique<OutlineInfo>();
12199 OI->OuterAllocBB = OuterAllocIP.getBlock();
12200 OI->EntryBB = AllocaBB;
12201 OI->ExitBB = ExitBB;
12202 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12203 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12204
12205 addOutlineInfo(std::move(OI));
12206 }
12207 Builder.SetInsertPoint(ExitBB);
12208
12209 return Builder.saveIP();
12210}
12211
12214 std::string VarName) {
12215 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12217 Names.size()),
12218 Names);
12219 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12220 M, MapNamesArrayInit->getType(),
12221 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12222 VarName);
12223 return MapNamesArrayGlobal;
12224}
12225
12226// Create all simple and struct types exposed by the runtime and remember
12227// the llvm::PointerTypes of them for easy access later.
12228void OpenMPIRBuilder::initializeTypes(Module &M) {
12229 LLVMContext &Ctx = M.getContext();
12230 StructType *T;
12231 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12232 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12233#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12234#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12235 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12236 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12237#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12238 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12239 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12240#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12241 T = StructType::getTypeByName(Ctx, StructName); \
12242 if (!T) \
12243 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12244 VarName = T; \
12245 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12246#include "llvm/Frontend/OpenMP/OMPKinds.def"
12247}
12248
12251 SmallVectorImpl<BasicBlock *> &BlockVector) {
12253 BlockSet.insert(EntryBB);
12254 BlockSet.insert(ExitBB);
12255
12256 Worklist.push_back(EntryBB);
12257 while (!Worklist.empty()) {
12258 BasicBlock *BB = Worklist.pop_back_val();
12259 BlockVector.push_back(BB);
12260 for (BasicBlock *SuccBB : successors(BB))
12261 if (BlockSet.insert(SuccBB).second)
12262 Worklist.push_back(SuccBB);
12263 }
12264}
12265
12266std::unique_ptr<CodeExtractor>
12268 bool ArgsInZeroAddressSpace,
12269 Twine Suffix) {
12270 return std::make_unique<CodeExtractor>(
12271 Blocks, /* DominatorTree */ nullptr,
12272 /* AggregateArgs */ true,
12273 /* BlockFrequencyInfo */ nullptr,
12274 /* BranchProbabilityInfo */ nullptr,
12275 /* AssumptionCache */ nullptr,
12276 /* AllowVarArgs */ true,
12277 /* AllowAlloca */ true,
12278 /* AllocationBlock*/ OuterAllocBB,
12279 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12280 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12281}
12282
12283std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12284 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12285 return std::make_unique<DeviceSharedMemCodeExtractor>(
12286 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12287 /* AggregateArgs */ true,
12288 /* BlockFrequencyInfo */ nullptr,
12289 /* BranchProbabilityInfo */ nullptr,
12290 /* AssumptionCache */ nullptr,
12291 /* AllowVarArgs */ true,
12292 /* AllowAlloca */ true,
12293 /* AllocationBlock*/ OuterAllocBB,
12294 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12296 : OuterDeallocBBs,
12297 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12298}
12299
12301 uint64_t Size, int32_t Flags,
12303 StringRef Name) {
12304 if (!Config.isGPU()) {
12307 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12308 return;
12309 }
12310 // TODO: Add support for global variables on the device after declare target
12311 // support.
12312 Function *Fn = dyn_cast<Function>(Addr);
12313 if (!Fn)
12314 return;
12315
12316 // Add a function attribute for the kernel.
12317 Fn->addFnAttr("kernel");
12318 if (T.isAMDGCN())
12319 Fn->addFnAttr("uniform-work-group-size");
12320 Fn->addFnAttr(Attribute::MustProgress);
12321}
12322
12323// We only generate metadata for function that contain target regions.
12326
12327 // If there are no entries, we don't need to do anything.
12328 if (OffloadInfoManager.empty())
12329 return;
12330
12331 LLVMContext &C = M.getContext();
12334 16>
12335 OrderedEntries(OffloadInfoManager.size());
12336
12337 // Auxiliary methods to create metadata values and strings.
12338 auto &&GetMDInt = [this](unsigned V) {
12339 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12340 };
12341
12342 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12343
12344 // Create the offloading info metadata node.
12345 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12346 auto &&TargetRegionMetadataEmitter =
12347 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12348 const TargetRegionEntryInfo &EntryInfo,
12350 // Generate metadata for target regions. Each entry of this metadata
12351 // contains:
12352 // - Entry 0 -> Kind of this type of metadata (0).
12353 // - Entry 1 -> Device ID of the file where the entry was identified.
12354 // - Entry 2 -> File ID of the file where the entry was identified.
12355 // - Entry 3 -> Mangled name of the function where the entry was
12356 // identified.
12357 // - Entry 4 -> Line in the file where the entry was identified.
12358 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12359 // - Entry 6 -> Order the entry was created.
12360 // The first element of the metadata node is the kind.
12361 Metadata *Ops[] = {
12362 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12363 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12364 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12365 GetMDInt(E.getOrder())};
12366
12367 // Save this entry in the right position of the ordered entries array.
12368 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12369
12370 // Add metadata to the named metadata node.
12371 MD->addOperand(MDNode::get(C, Ops));
12372 };
12373
12374 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12375
12376 // Create function that emits metadata for each device global variable entry;
12377 auto &&DeviceGlobalVarMetadataEmitter =
12378 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12379 StringRef MangledName,
12381 // Generate metadata for global variables. Each entry of this metadata
12382 // contains:
12383 // - Entry 0 -> Kind of this type of metadata (1).
12384 // - Entry 1 -> Mangled name of the variable.
12385 // - Entry 2 -> Declare target kind.
12386 // - Entry 3 -> Order the entry was created.
12387 // The first element of the metadata node is the kind.
12388 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12389 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12390
12391 // Save this entry in the right position of the ordered entries array.
12392 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12393 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12394
12395 // Add metadata to the named metadata node.
12396 MD->addOperand(MDNode::get(C, Ops));
12397 };
12398
12399 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12400 DeviceGlobalVarMetadataEmitter);
12401
12402 for (const auto &E : OrderedEntries) {
12403 assert(E.first && "All ordered entries must exist!");
12404 if (const auto *CE =
12406 E.first)) {
12407 if (!CE->getID() || !CE->getAddress()) {
12408 // Do not blame the entry if the parent funtion is not emitted.
12409 TargetRegionEntryInfo EntryInfo = E.second;
12410 StringRef FnName = EntryInfo.ParentName;
12411 if (!M.getNamedValue(FnName))
12412 continue;
12413 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12414 continue;
12415 }
12416 createOffloadEntry(CE->getID(), CE->getAddress(),
12417 /*Size=*/0, CE->getFlags(),
12419 } else if (const auto *CE = dyn_cast<
12421 E.first)) {
12424 CE->getFlags());
12425 switch (Flags) {
12428 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12429 continue;
12430 if (!CE->getAddress()) {
12431 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12432 continue;
12433 }
12434 // The vaiable has no definition - no need to add the entry.
12435 if (CE->getVarSize() == 0)
12436 continue;
12437 break;
12439 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12440 (!Config.isTargetDevice() && CE->getAddress())) &&
12441 "Declaret target link address is set.");
12442 if (Config.isTargetDevice())
12443 continue;
12444 if (!CE->getAddress()) {
12446 continue;
12447 }
12448 break;
12451 if (!CE->getAddress()) {
12452 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12453 continue;
12454 }
12455 break;
12456 default:
12457 break;
12458 }
12459
12460 // Hidden or internal symbols on the device are not externally visible.
12461 // We should not attempt to register them by creating an offloading
12462 // entry. Indirect variables are handled separately on the device.
12463 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12464 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12465 (Flags !=
12467 Flags != OffloadEntriesInfoManager::
12468 OMPTargetGlobalVarEntryIndirectVTable))
12469 continue;
12470
12471 // Indirect globals need to use a special name that doesn't match the name
12472 // of the associated host global.
12474 Flags ==
12476 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12477 Flags, CE->getLinkage(), CE->getVarName());
12478 else
12479 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12480 Flags, CE->getLinkage());
12481
12482 } else {
12483 llvm_unreachable("Unsupported entry kind.");
12484 }
12485 }
12486
12487 // Emit requires directive globals to a special entry so the runtime can
12488 // register them when the device image is loaded.
12489 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12490 // entries should be redesigned to better suit this use-case.
12491 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12495 ".requires", /*Size=*/0,
12497 Config.getRequiresFlags());
12498}
12499
12502 unsigned FileID, unsigned Line, unsigned Count) {
12503 raw_svector_ostream OS(Name);
12504 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12505 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12506 if (Count)
12507 OS << "_" << Count;
12508}
12509
12511 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12512 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12514 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12515 EntryInfo.Line, NewCount);
12516}
12517
12520 vfs::FileSystem &VFS,
12521 StringRef ParentName) {
12522 sys::fs::UniqueID ID(0xdeadf17e, 0);
12523 auto FileIDInfo = CallBack();
12524 uint64_t FileID = 0;
12525 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12526 ID = Status->getUniqueID();
12527 FileID = Status->getUniqueID().getFile();
12528 } else {
12529 // If the inode ID could not be determined, create a hash value
12530 // the current file name and use that as an ID.
12531 FileID = hash_value(std::get<0>(FileIDInfo));
12532 }
12533
12534 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12535 std::get<1>(FileIDInfo));
12536}
12537
12539 unsigned Offset = 0;
12540 for (uint64_t Remain =
12541 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12543 !(Remain & 1); Remain = Remain >> 1)
12544 Offset++;
12545 return Offset;
12546}
12547
12550 // Rotate by getFlagMemberOffset() bits.
12551 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12552 << getFlagMemberOffset());
12553}
12554
12557 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12558 // If the entry is PTR_AND_OBJ but has not been marked with the special
12559 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12560 // marked as MEMBER_OF.
12561 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12563 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12566 return;
12567
12568 // Entries with ATTACH are not members-of anything. They are handled
12569 // separately by the runtime after other maps have been handled.
12570 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12572 return;
12573
12574 // Reset the placeholder value to prepare the flag for the assignment of the
12575 // proper MEMBER_OF value.
12576 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12577 Flags |= MemberOfFlag;
12578}
12579
12583 bool IsDeclaration, bool IsExternallyVisible,
12584 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12585 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12586 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12587 std::function<Constant *()> GlobalInitializer,
12588 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12589 // TODO: convert this to utilise the IRBuilder Config rather than
12590 // a passed down argument.
12591 if (OpenMPSIMD)
12592 return nullptr;
12593
12596 CaptureClause ==
12598 Config.hasRequiresUnifiedSharedMemory())) {
12599 SmallString<64> PtrName;
12600 {
12601 raw_svector_ostream OS(PtrName);
12602 OS << MangledName;
12603 if (!IsExternallyVisible)
12604 OS << format("_%x", EntryInfo.FileID);
12605 OS << "_decl_tgt_ref_ptr";
12606 }
12607
12608 Value *Ptr = M.getNamedValue(PtrName);
12609
12610 if (!Ptr) {
12611 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12612 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12613
12614 auto *GV = cast<GlobalVariable>(Ptr);
12615 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12616
12617 if (!Config.isTargetDevice()) {
12618 if (GlobalInitializer)
12619 GV->setInitializer(GlobalInitializer());
12620 else
12621 GV->setInitializer(GlobalValue);
12622 }
12623
12625 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12626 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12627 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12628 }
12629
12630 return cast<Constant>(Ptr);
12631 }
12632
12633 return nullptr;
12634}
12635
12639 bool IsDeclaration, bool IsExternallyVisible,
12640 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12641 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12642 std::vector<Triple> TargetTriple,
12643 std::function<Constant *()> GlobalInitializer,
12644 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12645 Constant *Addr) {
12647 (TargetTriple.empty() && !Config.isTargetDevice()))
12648 return;
12649
12651 StringRef VarName;
12652 int64_t VarSize;
12654
12656 CaptureClause ==
12658 !Config.hasRequiresUnifiedSharedMemory()) {
12660 VarName = MangledName;
12661 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12662
12663 if (!IsDeclaration)
12664 VarSize = divideCeil(
12665 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12666 else
12667 VarSize = 0;
12668 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12669
12670 // This is a workaround carried over from Clang which prevents undesired
12671 // optimisation of internal variables.
12672 if (Config.isTargetDevice() &&
12673 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12674 // Do not create a "ref-variable" if the original is not also available
12675 // on the host.
12676 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12677 return;
12678
12679 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12680
12681 if (!M.getNamedValue(RefName)) {
12682 Constant *AddrRef =
12683 getOrCreateInternalVariable(Addr->getType(), RefName);
12684 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12685 GvAddrRef->setConstant(true);
12686 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12687 GvAddrRef->setInitializer(Addr);
12688 GeneratedRefs.push_back(GvAddrRef);
12689 }
12690 }
12691 } else {
12694 else
12696
12697 if (Config.isTargetDevice()) {
12698 VarName = (Addr) ? Addr->getName() : "";
12699 Addr = nullptr;
12700 } else {
12702 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12703 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12704 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12705 VarName = (Addr) ? Addr->getName() : "";
12706 }
12707 VarSize = M.getDataLayout().getPointerSize();
12709 }
12710
12711 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12712 Flags, Linkage);
12713}
12714
12715/// Loads all the offload entries information from the host IR
12716/// metadata.
12718 // If we are in target mode, load the metadata from the host IR. This code has
12719 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12720
12721 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12722 if (!MD)
12723 return;
12724
12725 for (MDNode *MN : MD->operands()) {
12726 auto &&GetMDInt = [MN](unsigned Idx) {
12727 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12728 return cast<ConstantInt>(V->getValue())->getZExtValue();
12729 };
12730
12731 auto &&GetMDString = [MN](unsigned Idx) {
12732 auto *V = cast<MDString>(MN->getOperand(Idx));
12733 return V->getString();
12734 };
12735
12736 switch (GetMDInt(0)) {
12737 default:
12738 llvm_unreachable("Unexpected metadata!");
12739 break;
12740 case OffloadEntriesInfoManager::OffloadEntryInfo::
12741 OffloadingEntryInfoTargetRegion: {
12742 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12743 /*DeviceID=*/GetMDInt(1),
12744 /*FileID=*/GetMDInt(2),
12745 /*Line=*/GetMDInt(4),
12746 /*Count=*/GetMDInt(5));
12747 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12748 /*Order=*/GetMDInt(6));
12749 break;
12750 }
12751 case OffloadEntriesInfoManager::OffloadEntryInfo::
12752 OffloadingEntryInfoDeviceGlobalVar:
12753 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12754 /*MangledName=*/GetMDString(1),
12756 /*Flags=*/GetMDInt(2)),
12757 /*Order=*/GetMDInt(3));
12758 break;
12759 }
12760 }
12761}
12762
12764 StringRef HostFilePath) {
12765 if (HostFilePath.empty())
12766 return;
12767
12768 auto Buf = VFS.getBufferForFile(HostFilePath);
12769 if (std::error_code Err = Buf.getError()) {
12770 report_fatal_error(("error opening host file from host file path inside of "
12771 "OpenMPIRBuilder: " +
12772 Err.message())
12773 .c_str());
12774 }
12775
12776 LLVMContext Ctx;
12778 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12779 if (std::error_code Err = M.getError()) {
12781 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12782 .c_str());
12783 }
12784
12785 loadOffloadInfoMetadata(*M.get());
12786}
12787
12790 llvm::StringRef Name) {
12791 Builder.restoreIP(Loc.IP);
12792
12793 BasicBlock *CurBB = Builder.GetInsertBlock();
12794 assert(CurBB &&
12795 "expected a valid insertion block for creating an iterator loop");
12796 Function *F = CurBB->getParent();
12797
12798 InsertPointTy SplitIP = Builder.saveIP();
12799 if (SplitIP.getPoint() == CurBB->end())
12800 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12801 SplitIP = InsertPointTy(CurBB, Terminator->getIterator());
12802
12803 BasicBlock *ContBB =
12804 splitBB(SplitIP, /*CreateBranch=*/false,
12805 Builder.getCurrentDebugLocation(), "omp.it.cont");
12806
12807 CanonicalLoopInfo *CLI =
12808 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12809 /*PreInsertBefore=*/ContBB,
12810 /*PostInsertBefore=*/ContBB, Name);
12811
12812 // Enter loop from original block.
12813 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12814
12815 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12816 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12817 T->eraseFromParent();
12818
12819 InsertPointTy BodyIP = CLI->getBodyIP();
12820 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12821 return Err;
12822
12823 // Body must either fallthrough to the latch or branch directly to it.
12824 if (Instruction *BodyTerminator = CLI->getBody()->getTerminatorOrNull()) {
12825 auto *BodyBr = dyn_cast<UncondBrInst>(BodyTerminator);
12826 if (!BodyBr || BodyBr->getSuccessor() != CLI->getLatch()) {
12828 "iterator bodygen must terminate the canonical body with an "
12829 "unconditional branch to the loop latch",
12831 }
12832 } else {
12833 // Ensure we end the loop body by jumping to the latch.
12834 Builder.SetInsertPoint(CLI->getBody());
12835 Builder.CreateBr(CLI->getLatch());
12836 }
12837
12838 // Link After -> ContBB
12839 Builder.SetInsertPoint(CLI->getAfter(), CLI->getAfter()->begin());
12840 if (!CLI->getAfter()->hasTerminator())
12841 Builder.CreateBr(ContBB);
12842
12843 return InsertPointTy{ContBB, ContBB->begin()};
12844}
12845
12846/// Mangle the parameter part of the vector function name according to
12847/// their OpenMP classification. The mangling function is defined in
12848/// section 4.5 of the AAVFABI(2021Q1).
12849static std::string mangleVectorParameters(
12851 SmallString<256> Buffer;
12852 llvm::raw_svector_ostream Out(Buffer);
12853 for (const auto &ParamAttr : ParamAttrs) {
12854 switch (ParamAttr.Kind) {
12856 Out << 'l';
12857 break;
12859 Out << 'R';
12860 break;
12862 Out << 'U';
12863 break;
12865 Out << 'L';
12866 break;
12868 Out << 'u';
12869 break;
12871 Out << 'v';
12872 break;
12873 }
12874 if (ParamAttr.HasVarStride)
12875 Out << "s" << ParamAttr.StrideOrArg;
12876 else if (ParamAttr.Kind ==
12878 ParamAttr.Kind ==
12880 ParamAttr.Kind ==
12882 ParamAttr.Kind ==
12884 // Don't print the step value if it is not present or if it is
12885 // equal to 1.
12886 if (ParamAttr.StrideOrArg < 0)
12887 Out << 'n' << -ParamAttr.StrideOrArg;
12888 else if (ParamAttr.StrideOrArg != 1)
12889 Out << ParamAttr.StrideOrArg;
12890 }
12891
12892 if (!!ParamAttr.Alignment)
12893 Out << 'a' << ParamAttr.Alignment;
12894 }
12895
12896 return std::string(Out.str());
12897}
12898
12900 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
12902 struct ISADataTy {
12903 char ISA;
12904 unsigned VecRegSize;
12905 };
12906 ISADataTy ISAData[] = {
12907 {'b', 128}, // SSE
12908 {'c', 256}, // AVX
12909 {'d', 256}, // AVX2
12910 {'e', 512}, // AVX512
12911 };
12913 switch (Branch) {
12915 Masked.push_back('N');
12916 Masked.push_back('M');
12917 break;
12919 Masked.push_back('N');
12920 break;
12922 Masked.push_back('M');
12923 break;
12924 }
12925 for (char Mask : Masked) {
12926 for (const ISADataTy &Data : ISAData) {
12928 llvm::raw_svector_ostream Out(Buffer);
12929 Out << "_ZGV" << Data.ISA << Mask;
12930 if (!VLENVal) {
12931 assert(NumElts && "Non-zero simdlen/cdtsize expected");
12932 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
12933 } else {
12934 Out << VLENVal;
12935 }
12936 Out << mangleVectorParameters(ParamAttrs);
12937 Out << '_' << Fn->getName();
12938 Fn->addFnAttr(Out.str());
12939 }
12940 }
12941}
12942
12943// Function used to add the attribute. The parameter `VLEN` is templated to
12944// allow the use of `x` when targeting scalable functions for SVE.
12945template <typename T>
12946static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
12947 char ISA, StringRef ParSeq,
12948 StringRef MangledName, bool OutputBecomesInput,
12949 llvm::Function *Fn) {
12950 SmallString<256> Buffer;
12951 llvm::raw_svector_ostream Out(Buffer);
12952 Out << Prefix << ISA << LMask << VLEN;
12953 if (OutputBecomesInput)
12954 Out << 'v';
12955 Out << ParSeq << '_' << MangledName;
12956 Fn->addFnAttr(Out.str());
12957}
12958
12959// Helper function to generate the Advanced SIMD names depending on the value
12960// of the NDS when simdlen is not present.
12961static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
12962 StringRef Prefix, char ISA,
12963 StringRef ParSeq, StringRef MangledName,
12964 bool OutputBecomesInput,
12965 llvm::Function *Fn) {
12966 switch (NDS) {
12967 case 8:
12968 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12969 OutputBecomesInput, Fn);
12970 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
12971 OutputBecomesInput, Fn);
12972 break;
12973 case 16:
12974 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
12975 OutputBecomesInput, Fn);
12976 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12977 OutputBecomesInput, Fn);
12978 break;
12979 case 32:
12980 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
12981 OutputBecomesInput, Fn);
12982 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
12983 OutputBecomesInput, Fn);
12984 break;
12985 case 64:
12986 case 128:
12987 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
12988 OutputBecomesInput, Fn);
12989 break;
12990 default:
12991 llvm_unreachable("Scalar type is too wide.");
12992 }
12993}
12994
12995/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
12997 llvm::Function *Fn, unsigned UserVLEN,
12999 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
13000 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
13001
13002 // Sort out parameter sequence.
13003 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
13004 StringRef Prefix = "_ZGV";
13005 StringRef MangledName = Fn->getName();
13006
13007 // Generate simdlen from user input (if any).
13008 if (UserVLEN) {
13009 if (ISA == 's') {
13010 // SVE generates only a masked function.
13011 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13012 OutputBecomesInput, Fn);
13013 return;
13014 }
13015
13016 switch (Branch) {
13018 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13019 OutputBecomesInput, Fn);
13020 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13021 OutputBecomesInput, Fn);
13022 break;
13024 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13025 OutputBecomesInput, Fn);
13026 break;
13028 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13029 OutputBecomesInput, Fn);
13030 break;
13031 }
13032 return;
13033 }
13034
13035 if (ISA == 's') {
13036 // SVE, section 3.4.1, item 1.
13037 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13038 OutputBecomesInput, Fn);
13039 return;
13040 }
13041
13042 switch (Branch) {
13044 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13045 MangledName, OutputBecomesInput, Fn);
13046 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13047 MangledName, OutputBecomesInput, Fn);
13048 break;
13050 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13051 MangledName, OutputBecomesInput, Fn);
13052 break;
13054 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13055 MangledName, OutputBecomesInput, Fn);
13056 break;
13057 }
13058}
13059
13060//===----------------------------------------------------------------------===//
13061// OffloadEntriesInfoManager
13062//===----------------------------------------------------------------------===//
13063
13065 return OffloadEntriesTargetRegion.empty() &&
13066 OffloadEntriesDeviceGlobalVar.empty();
13067}
13068
13069unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13070 const TargetRegionEntryInfo &EntryInfo) const {
13071 auto It = OffloadEntriesTargetRegionCount.find(
13072 getTargetRegionEntryCountKey(EntryInfo));
13073 if (It == OffloadEntriesTargetRegionCount.end())
13074 return 0;
13075 return It->second;
13076}
13077
13078void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13079 const TargetRegionEntryInfo &EntryInfo) {
13080 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13081 EntryInfo.Count + 1;
13082}
13083
13084/// Initialize target region entry.
13086 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13087 OffloadEntriesTargetRegion[EntryInfo] =
13088 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13090 ++OffloadingEntriesNum;
13091}
13092
13094 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13096 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13097
13098 // Update the EntryInfo with the next available count for this location.
13099 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13100
13101 // If we are emitting code for a target, the entry is already initialized,
13102 // only has to be registered.
13103 if (OMPBuilder->Config.isTargetDevice()) {
13104 // This could happen if the device compilation is invoked standalone.
13105 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13106 return;
13107 }
13108 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13109 Entry.setAddress(Addr);
13110 Entry.setID(ID);
13111 Entry.setFlags(Flags);
13112 } else {
13114 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13115 return;
13116 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13117 "Target region entry already registered!");
13118 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13119 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13120 ++OffloadingEntriesNum;
13121 }
13122 incrementTargetRegionEntryInfoCount(EntryInfo);
13123}
13124
13126 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13127
13128 // Update the EntryInfo with the next available count for this location.
13129 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13130
13131 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13132 if (It == OffloadEntriesTargetRegion.end()) {
13133 return false;
13134 }
13135 // Fail if this entry is already registered.
13136 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13137 return false;
13138 return true;
13139}
13140
13142 const OffloadTargetRegionEntryInfoActTy &Action) {
13143 // Scan all target region entries and perform the provided action.
13144 for (const auto &It : OffloadEntriesTargetRegion) {
13145 Action(It.first, It.second);
13146 }
13147}
13148
13150 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13151 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13152 ++OffloadingEntriesNum;
13153}
13154
13156 StringRef VarName, Constant *Addr, int64_t VarSize,
13158 if (OMPBuilder->Config.isTargetDevice()) {
13159 // This could happen if the device compilation is invoked standalone.
13160 if (!hasDeviceGlobalVarEntryInfo(VarName))
13161 return;
13162 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13163 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13164 if (Entry.getVarSize() == 0) {
13165 Entry.setVarSize(VarSize);
13166 Entry.setLinkage(Linkage);
13167 }
13168 return;
13169 }
13170 Entry.setVarSize(VarSize);
13171 Entry.setLinkage(Linkage);
13172 Entry.setAddress(Addr);
13173 } else {
13174 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13175 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13176 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13177 "Entry not initialized!");
13178 if (Entry.getVarSize() == 0) {
13179 Entry.setVarSize(VarSize);
13180 Entry.setLinkage(Linkage);
13181 }
13182 return;
13183 }
13185 Flags ==
13187 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13188 Addr, VarSize, Flags, Linkage,
13189 VarName.str());
13190 else
13191 OffloadEntriesDeviceGlobalVar.try_emplace(
13192 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13193 ++OffloadingEntriesNum;
13194 }
13195}
13196
13199 // Scan all target region entries and perform the provided action.
13200 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13201 Action(E.getKey(), E.getValue());
13202}
13203
13204//===----------------------------------------------------------------------===//
13205// CanonicalLoopInfo
13206//===----------------------------------------------------------------------===//
13207
13208void CanonicalLoopInfo::collectControlBlocks(
13210 // We only count those BBs as control block for which we do not need to
13211 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13212 // flow. For consistency, this also means we do not add the Body block, which
13213 // is just the entry to the body code.
13214 BBs.reserve(BBs.size() + 6);
13215 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13216}
13217
13219 assert(isValid() && "Requires a valid canonical loop");
13220 for (BasicBlock *Pred : predecessors(Header)) {
13221 if (Pred != Latch)
13222 return Pred;
13223 }
13224 llvm_unreachable("Missing preheader");
13225}
13226
13227void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13228 assert(isValid() && "Requires a valid canonical loop");
13229
13230 Instruction *CmpI = &getCond()->front();
13231 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13232 CmpI->setOperand(1, TripCount);
13233
13234#ifndef NDEBUG
13235 assertOK();
13236#endif
13237}
13238
13239void CanonicalLoopInfo::mapIndVar(
13240 llvm::function_ref<Value *(Instruction *)> Updater) {
13241 assert(isValid() && "Requires a valid canonical loop");
13242
13243 Instruction *OldIV = getIndVar();
13244
13245 // Record all uses excluding those introduced by the updater. Uses by the
13246 // CanonicalLoopInfo itself to keep track of the number of iterations are
13247 // excluded.
13248 SmallVector<Use *> ReplacableUses;
13249 for (Use &U : OldIV->uses()) {
13250 auto *User = dyn_cast<Instruction>(U.getUser());
13251 if (!User)
13252 continue;
13253 if (User->getParent() == getCond())
13254 continue;
13255 if (User->getParent() == getLatch())
13256 continue;
13257 ReplacableUses.push_back(&U);
13258 }
13259
13260 // Run the updater that may introduce new uses
13261 Value *NewIV = Updater(OldIV);
13262
13263 // Replace the old uses with the value returned by the updater.
13264 for (Use *U : ReplacableUses)
13265 U->set(NewIV);
13266
13267#ifndef NDEBUG
13268 assertOK();
13269#endif
13270}
13271
13273#ifndef NDEBUG
13274 // No constraints if this object currently does not describe a loop.
13275 if (!isValid())
13276 return;
13277
13278 BasicBlock *Preheader = getPreheader();
13279 BasicBlock *Body = getBody();
13280 BasicBlock *After = getAfter();
13281
13282 // Verify standard control-flow we use for OpenMP loops.
13283 assert(Preheader);
13284 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13285 "Preheader must terminate with unconditional branch");
13286 assert(Preheader->getSingleSuccessor() == Header &&
13287 "Preheader must jump to header");
13288
13289 assert(Header);
13290 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13291 "Header must terminate with unconditional branch");
13292 assert(Header->getSingleSuccessor() == Cond &&
13293 "Header must jump to exiting block");
13294
13295 assert(Cond);
13296 assert(Cond->getSinglePredecessor() == Header &&
13297 "Exiting block only reachable from header");
13298
13299 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13300 "Exiting block must terminate with conditional branch");
13301 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13302 "Exiting block's first successor jump to the body");
13303 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13304 "Exiting block's second successor must exit the loop");
13305
13306 assert(Body);
13307 assert(Body->getSinglePredecessor() == Cond &&
13308 "Body only reachable from exiting block");
13309 assert(!isa<PHINode>(Body->front()));
13310
13311 assert(Latch);
13312 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13313 "Latch must terminate with unconditional branch");
13314 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13315 // TODO: To support simple redirecting of the end of the body code that has
13316 // multiple; introduce another auxiliary basic block like preheader and after.
13317 assert(Latch->getSinglePredecessor() != nullptr);
13318 assert(!isa<PHINode>(Latch->front()));
13319
13320 assert(Exit);
13321 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13322 "Exit block must terminate with unconditional branch");
13323 assert(Exit->getSingleSuccessor() == After &&
13324 "Exit block must jump to after block");
13325
13326 assert(After);
13327 assert(After->getSinglePredecessor() == Exit &&
13328 "After block only reachable from exit block");
13329 assert(After->empty() || !isa<PHINode>(After->front()));
13330
13331 Instruction *IndVar = getIndVar();
13332 assert(IndVar && "Canonical induction variable not found?");
13333 assert(isa<IntegerType>(IndVar->getType()) &&
13334 "Induction variable must be an integer");
13335 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13336 "Induction variable must be a PHI in the loop header");
13337 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13338 assert(
13339 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13340 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13341
13342 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13343 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13344 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13345 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13346 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13347 ->isOne());
13348
13349 Value *TripCount = getTripCount();
13350 assert(TripCount && "Loop trip count not found?");
13351 assert(IndVar->getType() == TripCount->getType() &&
13352 "Trip count and induction variable must have the same type");
13353
13354 auto *CmpI = cast<CmpInst>(&Cond->front());
13355 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13356 "Exit condition must be a signed less-than comparison");
13357 assert(CmpI->getOperand(0) == IndVar &&
13358 "Exit condition must compare the induction variable");
13359 assert(CmpI->getOperand(1) == TripCount &&
13360 "Exit condition must compare with the trip count");
13361#endif
13362}
13363
13365 Header = nullptr;
13366 Cond = nullptr;
13367 Latch = nullptr;
13368 Exit = nullptr;
13369}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
Rewrite undef for PHI
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static cl::opt< ITMode > IT(cl::desc("IT block support"), cl::Hidden, cl::init(DefaultIT), cl::values(clEnumValN(DefaultIT, "arm-default-it", "Generate any type of IT block"), clEnumValN(RestrictedIT, "arm-restrict-it", "Disallow complex IT blocks")))
Expand Atomic instructions
@ ParamAttr
This file contains the simple types necessary to represent the attributes associated with functions a...
static const Function * getParent(const Value *V)
#define X(NUM, ENUM, NAME)
Definition ELF.h:856
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Hexagon Common GEP
Hexagon Hardware Loops
This file provides various utilities for inspecting and working with the control flow graph in LLVM I...
This header defines various interfaces for pass management in LLVM.
iv Induction Variable Users
Definition IVUsers.cpp:48
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
static LVOptions Options
Definition LVOptions.cpp:25
static bool isZero(Value *V, const DataLayout &DL, DominatorTree *DT, AssumptionCache *AC)
Definition Lint.cpp:539
static cl::opt< unsigned > TileSize("fuse-matrix-tile-size", cl::init(4), cl::Hidden, cl::desc("Tile size for matrix instruction fusion using square-shaped tiles."))
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
This file contains the declarations for metadata subclasses.
#define T
uint64_t IntrinsicInst * II
#define OMP_KERNEL_ARG_VERSION
Provides definitions for Target specific Grid Values.
static Value * removeASCastIfPresent(Value *V)
static void createTargetLoopWorkshareCall(OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType, BasicBlock *InsertBlock, Value *Ident, Value *LoopBodyArg, Value *TripCount, Function &LoopBodyFn, bool NoLoop)
Value * createFakeIntVal(IRBuilderBase &Builder, OpenMPIRBuilder::InsertPointTy OuterAllocaIP, llvm::SmallVectorImpl< Instruction * > &ToBeDeleted, OpenMPIRBuilder::InsertPointTy InnerAllocaIP, const Twine &Name="", bool AsPtr=true, bool Is64Bit=false)
static Function * createTargetParallelWrapper(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn)
Create wrapper function used to gather the outlined function's argument structure from a shared buffe...
static void redirectTo(BasicBlock *Source, BasicBlock *Target, DebugLoc DL)
Make Source branch to Target.
static FunctionCallee getKmpcDistForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void applyParallelAccessesMetadata(CanonicalLoopInfo *CLI, LLVMContext &Ctx, Loop *Loop, LoopInfo &LoopInfo, SmallVector< Metadata * > &LoopMDList)
static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix, char ISA, StringRef ParSeq, StringRef MangledName, bool OutputBecomesInput, llvm::Function *Fn)
static FunctionCallee getKmpcForDynamicFiniForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for finalizing the dynamic loop using depending on type.
static Expected< Function * > createOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, StringRef FuncName, SmallVectorImpl< Value * > &Inputs, OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc, OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB)
static void FixupDebugInfoForOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func, DenseMap< Value *, std::tuple< Value *, unsigned > > &ValueReplacementMap)
static OMPScheduleType getOpenMPOrderingScheduleType(OMPScheduleType BaseScheduleType, bool HasOrderedClause)
Adds ordering modifier flags to schedule type.
static OMPScheduleType getOpenMPMonotonicityScheduleType(OMPScheduleType ScheduleType, bool HasSimdModifier, bool HasMonotonic, bool HasNonmonotonic, bool HasOrderedClause)
Adds monotonicity modifier flags to schedule type.
static std::string mangleVectorParameters(ArrayRef< llvm::OpenMPIRBuilder::DeclareSimdAttrTy > ParamAttrs)
Mangle the parameter part of the vector function name according to their OpenMP classification.
static bool isGenericKernel(Function &Fn)
static void workshareLoopTargetCallback(OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident, Function &OutlinedFn, const SmallVector< Instruction *, 4 > &ToBeDeleted, WorksharingLoopType LoopType, bool NoLoop)
static bool isValidWorkshareLoopScheduleType(OMPScheduleType SchedType)
static bool isAtomicableReductionSet(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos)
static llvm::CallInst * emitNoUnwindRuntimeCall(IRBuilder<> &Builder, llvm::FunctionCallee Callee, ArrayRef< llvm::Value * > Args, const llvm::Twine &Name)
static Error populateReductionFunction(Function *ReductionFunc, ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, IRBuilder<> &Builder, ArrayRef< bool > IsByRef, bool IsGPU)
static Function * getFreshReductionFunc(Module &M)
static void raiseUserConstantDataAllocasToEntryBlock(IRBuilderBase &Builder, Function *Function)
static FunctionCallee getKmpcForDynamicNextForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for updating the next loop using OpenMP dynamic scheduling depending...
static bool isConflictIP(IRBuilder<>::InsertPoint IP1, IRBuilder<>::InsertPoint IP2)
Return whether IP1 and IP2 are ambiguous, i.e.
static void checkReductionInfos(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, bool IsGPU)
static Type * getOffloadingArrayType(Value *V)
static OMPScheduleType getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasDistScheduleChunks)
Determine which scheduling algorithm to use, determined from schedule clause arguments.
static OMPScheduleType computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasMonotonicModifier, bool HasNonmonotonicModifier, bool HasOrderedClause, bool HasDistScheduleChunks)
Determine the schedule type using schedule and ordering clause arguments.
static FunctionCallee getKmpcForDynamicInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for initializing loop bounds using OpenMP dynamic scheduling dependi...
static std::optional< omp::OMPTgtExecModeFlags > getTargetKernelExecMode(Function &Kernel)
Given a function, if it represents the entry point of a target kernel, this returns the execution mod...
static StructType * createTaskWithPrivatesTy(OpenMPIRBuilder &OMPIRBuilder, ArrayRef< Value * > OffloadingArraysToPrivatize)
static cl::opt< double > UnrollThresholdFactor("openmp-ir-builder-unroll-threshold-factor", cl::Hidden, cl::desc("Factor for the unroll threshold to account for code " "simplifications still taking place"), cl::init(1.5))
static cl::opt< bool > UseDefaultMaxThreads("openmp-ir-builder-use-default-max-threads", cl::Hidden, cl::desc("Use a default max threads if none is provided."), cl::init(true))
static int32_t computeHeuristicUnrollFactor(CanonicalLoopInfo *CLI)
Heuristically determine the best-performant unroll factor for CLI.
static void emitTargetCall(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, OpenMPIRBuilder::InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, OpenMPIRBuilder::TargetDataInfo &Info, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, const OpenMPIRBuilder::TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID, SmallVectorImpl< Value * > &Args, OpenMPIRBuilder::GenMapInfoCallbackTy GenMapInfoCB, OpenMPIRBuilder::CustomMapperCallbackTy CustomMapperCB, const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait, Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback)
static Value * emitTaskDependencies(OpenMPIRBuilder &OMPBuilder, const SmallVectorImpl< OpenMPIRBuilder::DependData > &Dependencies)
static Error emitTargetOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry, TargetRegionEntryInfo &EntryInfo, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, Function *&OutlinedFn, Constant *&OutlinedFnID, SmallVectorImpl< Value * > &Inputs, OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc, OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB)
static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value, bool Min)
static OpenMPIRBuilder::InsertPointTy getInsertPointAfterInstr(Instruction *I)
static void redirectAllPredecessorsTo(BasicBlock *OldTarget, BasicBlock *NewTarget, DebugLoc DL)
Redirect all edges that branch to OldTarget to NewTarget.
static void hoistNonEntryAllocasToEntryBlock(llvm::BasicBlock &Block)
static std::unique_ptr< TargetMachine > createTargetMachine(Function *F, CodeGenOptLevel OptLevel)
Create the TargetMachine object to query the backend for optimization preferences.
static FunctionCallee getKmpcForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup, LoopInfo &LI)
Attach llvm.access.group metadata to the memref instructions of Block.
static void addBasicBlockMetadata(BasicBlock *BB, ArrayRef< Metadata * > Properties)
Attach metadata Properties to the basic block described by BB.
static void restoreIPandDebugLoc(llvm::IRBuilderBase &Builder, llvm::IRBuilderBase::InsertPoint IP)
This is a wrapper over IRBuilderBase::restoreIP that also restores a current debug location when the ...
static LoadInst * loadSharedDataFromTaskDescriptor(OpenMPIRBuilder &OMPIRBuilder, IRBuilderBase &Builder, Value *TaskWithPrivates, Type *TaskWithPrivatesTy)
Given a task descriptor, TaskWithPrivates, return the pointer to the block of pointers containing sha...
static cl::opt< bool > OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden, cl::desc("Use optimistic attributes describing " "'as-if' properties of runtime calls."), cl::init(false))
static bool hasGridValue(const Triple &T)
static FunctionCallee getKmpcForStaticLoopForType(Type *Ty, OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType)
static const omp::GV & getGridValue(const Triple &T, Function *Kernel)
static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask, StringRef Prefix, char ISA, StringRef ParSeq, StringRef MangledName, bool OutputBecomesInput, llvm::Function *Fn)
static Function * emitTargetTaskProxyFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI, StructType *PrivatesTy, StructType *TaskWithPrivatesTy, const size_t NumOffloadingArrays, const int SharedArgsOperandNo)
Create an entry point for a target task with the following.
static void addLoopMetadata(CanonicalLoopInfo *Loop, ArrayRef< Metadata * > Properties)
Attach loop metadata Properties to the loop described by Loop.
static AtomicOrdering TransformReleaseAcquireRelease(AtomicOrdering AO)
static void removeUnusedBlocksFromParent(ArrayRef< BasicBlock * > BBs)
static void targetParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition, Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr, Value *ThreadID, const SmallVector< Instruction *, 4 > &ToBeDeleted)
static void hostParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, Value *Ident, Value *IfCondition, Instruction *PrivTID, AllocaInst *PrivTIDAddr, const SmallVector< Instruction *, 4 > &ToBeDeleted)
#define P(N)
FunctionAnalysisManager FAM
Function * Fun
This file defines the Pass Instrumentation classes that provide instrumentation points into the pass ...
const SmallVectorImpl< MachineOperand > & Cond
Remove Loads Into Fake Uses
static bool isValid(const char C)
Returns true if C is a valid mangled character: <0-9a-zA-Z_>.
SmallPtrSet< BasicBlock *, 0 > BlockSet
This file implements the SmallBitVector class.
This file defines the SmallSet class.
This file defines less commonly used SmallVector utilities.
This file contains some functions that are useful when dealing with strings.
#define LLVM_DEBUG(...)
Definition Debug.h:119
static SymbolRef::Type getType(const Symbol *Sym)
Definition TapiFile.cpp:39
Defines the virtual file system interface vfs::FileSystem.
Value * RHS
Value * LHS
static cl::opt< unsigned > MaxThreads("xcore-max-threads", cl::Optional, cl::desc("Maximum number of threads (for emulation thread-local storage)"), cl::Hidden, cl::value_desc("number"), cl::init(8))
static const uint32_t IV[8]
Definition blake3_impl.h:83
The Input class is used to parse a yaml document into in-memory structs and vectors.
Class for arbitrary precision integers.
Definition APInt.h:78
static APInt getSignedMaxValue(unsigned numBits)
Gets maximum signed value of APInt for a specific bit width.
Definition APInt.h:206
An arbitrary precision integer that knows its signedness.
Definition APSInt.h:24
static APSInt getUnsigned(uint64_t X)
Definition APSInt.h:349
This class represents a conversion between pointers from one address space to another.
an instruction to allocate memory on the stack
Align getAlign() const
Return the alignment of the memory that is being allocated by the instruction.
PointerType * getType() const
Overload to return most specific pointer type.
Type * getAllocatedType() const
Return the type that is being allocated by the instruction.
unsigned getAddressSpace() const
Return the address space for the allocation.
LLVM_ABI std::optional< TypeSize > getAllocationSize(const DataLayout &DL) const
Get allocation size in bytes.
LLVM_ABI bool isArrayAllocation() const
Return true if there is an allocation size parameter to the allocation instruction that is not 1.
void setAlignment(Align Align)
const Value * getArraySize() const
Get the number of elements allocated.
bool registerPass(PassBuilderT &&PassBuilder)
Register an analysis pass with the manager.
This class represents an incoming formal argument to a Function.
Definition Argument.h:32
unsigned getArgNo() const
Return the index of this formal argument in its containing function.
Definition Argument.h:50
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
iterator end() const
Definition ArrayRef.h:130
size_t size() const
Get the array size.
Definition ArrayRef.h:141
bool empty() const
Check if the array is empty.
Definition ArrayRef.h:136
Class to represent array types.
static LLVM_ABI ArrayType * get(Type *ElementType, uint64_t NumElements)
This static method is the primary way to construct an ArrayType.
A function analysis which provides an AssumptionCache.
LLVM_ABI AssumptionCache run(Function &F, FunctionAnalysisManager &)
A cache of @llvm.assume calls within a function.
An instruction that atomically checks whether a specified value is in a memory location,...
void setWeak(bool IsWeak)
static AtomicOrdering getStrongestFailureOrdering(AtomicOrdering SuccessOrdering)
Returns the strongest permitted ordering on failure, given the desired ordering on success.
LLVM_ABI std::pair< LoadInst *, AllocaInst * > EmitAtomicLoadLibcall(AtomicOrdering AO)
Definition Atomic.cpp:109
LLVM_ABI void EmitAtomicStoreLibcall(AtomicOrdering AO, Value *Source)
Definition Atomic.cpp:150
an instruction that atomically reads a memory location, combines it with another value,...
BinOp
This enumeration lists the possible modifications atomicrmw can make.
@ Add
*p = old + v
@ FAdd
*p = old + v
@ USubCond
Subtract only if no unsigned overflow.
@ FMinimum
*p = minimum(old, v) minimum matches the behavior of llvm.minimum.
@ Min
*p = old <signed v ? old : v
@ Sub
*p = old - v
@ And
*p = old & v
@ Xor
*p = old ^ v
@ USubSat
*p = usub.sat(old, v) usub.sat matches the behavior of llvm.usub.sat.
@ FMaximum
*p = maximum(old, v) maximum matches the behavior of llvm.maximum.
@ FSub
*p = old - v
@ UIncWrap
Increment one up to a maximum value.
@ Max
*p = old >signed v ? old : v
@ UMin
*p = old <unsigned v ? old : v
@ FMin
*p = minnum(old, v) minnum matches the behavior of llvm.minnum.
@ UMax
*p = old >unsigned v ? old : v
@ FMaximumNum
*p = maximumnum(old, v) maximumnum matches the behavior of llvm.maximumnum.
@ FMax
*p = maxnum(old, v) maxnum matches the behavior of llvm.maxnum.
@ UDecWrap
Decrement one until a minimum value or zero.
@ FMinimumNum
*p = minimumnum(old, v) minimumnum matches the behavior of llvm.minimumnum.
@ Nand
*p = ~(old & v)
This class holds the attributes for a particular argument, parameter, function, or return value.
Definition Attributes.h:407
LLVM_ABI AttributeSet addAttributes(LLVMContext &C, AttributeSet AS) const
Add attributes to the attribute set.
LLVM_ABI AttributeSet addAttribute(LLVMContext &C, Attribute::AttrKind Kind) const
Add an argument attribute.
static LLVM_ABI Attribute getWithAlignment(LLVMContext &Context, Align Alignment)
Return a uniquified Attribute object that has the specific alignment set.
LLVM Basic Block Representation.
Definition BasicBlock.h:62
LLVM_ABI void replaceSuccessorsPhiUsesWith(BasicBlock *Old, BasicBlock *New)
Update all phi nodes in this basic block's successors to refer to basic block New instead of basic bl...
iterator end()
Definition BasicBlock.h:459
iterator begin()
Instruction iterator methods.
Definition BasicBlock.h:446
LLVM_ABI const_iterator getFirstInsertionPt() const
Returns an iterator to the first instruction in this block that is suitable for inserting a non-PHI i...
LLVM_ABI BasicBlock * splitBasicBlock(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction.
const Function * getParent() const
Return the enclosing method, or null if none.
Definition BasicBlock.h:213
reverse_iterator rbegin()
Definition BasicBlock.h:462
bool hasTerminator() const LLVM_READONLY
Returns whether the block has a terminator.
Definition BasicBlock.h:232
bool empty() const
Definition BasicBlock.h:468
const Instruction & back() const
Definition BasicBlock.h:471
LLVM_ABI BasicBlock * splitBasicBlockBefore(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction and insert the new basic blo...
LLVM_ABI InstListType::const_iterator getFirstNonPHIIt() const
Returns an iterator to the first instruction in this block that is not a PHINode instruction.
LLVM_ABI void insertDbgRecordBefore(DbgRecord *DR, InstListType::iterator Here)
Insert a DbgRecord into a block at the position given by Here.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
LLVM_ABI InstListType::const_iterator getFirstNonPHIOrDbg(bool SkipPseudoOp=true) const
Returns a pointer to the first instruction in this block that is not a PHINode or a debug intrinsic,...
LLVM_ABI const BasicBlock * getUniqueSuccessor() const
Return the successor of this block if it has a unique successor.
LLVM_ABI const BasicBlock * getSinglePredecessor() const
Return the predecessor of this block if it has a single predecessor block.
const Instruction & front() const
Definition BasicBlock.h:469
InstListType::reverse_iterator reverse_iterator
Definition BasicBlock.h:172
LLVM_ABI const BasicBlock * getUniquePredecessor() const
Return the predecessor of this block if it has a unique predecessor block.
const Instruction * getTerminatorOrNull() const LLVM_READONLY
Returns the terminator instruction if the block is well formed or null if the block is not well forme...
Definition BasicBlock.h:248
LLVM_ABI const BasicBlock * getSingleSuccessor() const
Return the successor of this block if it has a single successor.
LLVM_ABI SymbolTableList< BasicBlock >::iterator eraseFromParent()
Unlink 'this' from the containing function and delete it.
reverse_iterator rend()
Definition BasicBlock.h:464
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
LLVM_ABI LLVMContext & getContext() const
Get the context in which this basic block lives.
void moveBefore(BasicBlock *MovePos)
Unlink this basic block from its current function and insert it into the function that MovePos lives ...
Definition BasicBlock.h:373
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction; assumes that the block is well-formed.
Definition BasicBlock.h:237
void splice(BasicBlock::iterator ToIt, BasicBlock *FromBB)
Transfer all instructions from FromBB to this basic block at ToIt.
Definition BasicBlock.h:644
LLVM_ABI void removePredecessor(BasicBlock *Pred, bool KeepOneInputPHIs=false)
Update PHI nodes in this BasicBlock before removal of predecessor Pred.
void setDoesNotThrow()
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
Value * getArgOperand(unsigned i) const
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
unsigned arg_size() const
This class represents a function call, abstracting a target machine's calling convention.
Class to represented the control flow structure of an OpenMP canonical loop.
Value * getTripCount() const
Returns the llvm::Value containing the number of loop iterations.
BasicBlock * getHeader() const
The header is the entry for each iteration.
LLVM_ABI void assertOK() const
Consistency self-check.
Type * getIndVarType() const
Return the type of the induction variable (and the trip count).
BasicBlock * getBody() const
The body block is the single entry for a loop iteration and not controlled by CanonicalLoopInfo.
bool isValid() const
Returns whether this object currently represents the IR of a loop.
void setLastIter(Value *IterVar)
Sets the last iteration variable for this loop.
OpenMPIRBuilder::InsertPointTy getAfterIP() const
Return the insertion point for user code after the loop.
OpenMPIRBuilder::InsertPointTy getBodyIP() const
Return the insertion point for user code in the body.
BasicBlock * getAfter() const
The after block is intended for clean-up code such as lifetime end markers.
Function * getFunction() const
LLVM_ABI void invalidate()
Invalidate this loop.
BasicBlock * getLatch() const
Reaching the latch indicates the end of the loop body code.
OpenMPIRBuilder::InsertPointTy getPreheaderIP() const
Return the insertion point for user code before the loop.
BasicBlock * getCond() const
The condition block computes whether there is another loop iteration.
BasicBlock * getExit() const
Reaching the exit indicates no more iterations are being executed.
LLVM_ABI BasicBlock * getPreheader() const
The preheader ensures that there is only a single edge entering the loop.
Instruction * getIndVar() const
Returns the instruction representing the current logical induction variable.
Predicate
This enumeration lists the possible predicates for CmpInst subclasses.
Definition InstrTypes.h:740
@ ICMP_SLT
signed less than
Definition InstrTypes.h:769
@ ICMP_SLE
signed less or equal
Definition InstrTypes.h:770
@ FCMP_OLT
0 1 0 0 True if ordered and less than
Definition InstrTypes.h:746
@ FCMP_OGT
0 0 1 0 True if ordered and greater than
Definition InstrTypes.h:744
@ ICMP_UGT
unsigned greater than
Definition InstrTypes.h:763
@ ICMP_SGT
signed greater than
Definition InstrTypes.h:767
@ ICMP_ULT
unsigned less than
Definition InstrTypes.h:765
@ ICMP_NE
not equal
Definition InstrTypes.h:762
@ ICMP_ULE
unsigned less or equal
Definition InstrTypes.h:766
A cache for the CodeExtractor analysis.
Utility class for extracting code into a new function.
static LLVM_ABI Constant * get(ArrayType *T, ArrayRef< Constant * > V)
static ConstantAsMetadata * get(Constant *C)
Definition Metadata.h:537
static Constant * get(LLVMContext &Context, ArrayRef< ElementTy > Elts)
get() constructor - Return a constant with array type with an element count and element type matching...
Definition Constants.h:878
static LLVM_ABI Constant * getString(LLVMContext &Context, StringRef Initializer, bool AddNull=true, bool ByteString=false)
This method constructs a CDS and initializes it with a text string.
static LLVM_ABI Constant * getPointerCast(Constant *C, Type *Ty)
Create a BitCast, AddrSpaceCast, or a PtrToInt cast constant expression.
static LLVM_ABI Constant * getTruncOrBitCast(Constant *C, Type *Ty)
static LLVM_ABI Constant * getPointerBitCastOrAddrSpaceCast(Constant *C, Type *Ty)
Create a BitCast or AddrSpaceCast for a pointer type depending on the address space.
static LLVM_ABI Constant * getSizeOf(Type *Ty)
getSizeOf constant expr - computes the (alloc) size of a type (in address-units, not bits) in a targe...
static LLVM_ABI Constant * getAddrSpaceCast(Constant *C, Type *Ty, bool OnlyIfReduced=false)
static LLVM_ABI ConstantFP * getZero(Type *Ty, bool Negative=false)
This is the shared class of boolean and integer constants.
Definition Constants.h:87
static ConstantInt * getSigned(IntegerType *Ty, int64_t V, bool ImplicitTrunc=false)
Return a ConstantInt with the specified value for the specified type.
Definition Constants.h:135
static LLVM_ABI ConstantPointerNull * get(PointerType *T)
Static factory methods - Return objects of the specified value.
static LLVM_ABI Constant * get(StructType *T, ArrayRef< Constant * > V)
This is an important base class in LLVM.
Definition Constant.h:43
static LLVM_ABI Constant * getAllOnesValue(Type *Ty)
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
DILocalScope * getScope() const
Get the local scope for this variable.
DINodeArray getAnnotations() const
DIFile * getFile() const
Subprogram description. Uses SubclassData1.
Base class for types.
uint32_t getAlignInBits() const
DIFile * getFile() const
DIType * getType() const
unsigned getLine() const
StringRef getName() const
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
TypeSize getTypeStoreSize(Type *Ty) const
Returns the maximum number of bytes that may be overwritten by storing the specified type.
Definition DataLayout.h:579
Record of a variable value-assignment, aka a non instruction representation of the dbg....
A debug info location.
Definition DebugLoc.h:126
Analysis pass which computes a DominatorTree.
Definition Dominators.h:241
LLVM_ABI DominatorTree run(Function &F, FunctionAnalysisManager &)
Run the analysis pass over a function and produce a dominator tree.
bool properlyDominates(const DomTreeNodeBase< NodeT > *A, const DomTreeNodeBase< NodeT > *B) const
properlyDominates - Returns true iff A dominates B and A != B.
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Definition Dominators.h:122
Represents either an error or a value T.
Definition ErrorOr.h:56
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
reference get()
Returns a reference to the stored T value.
Definition Error.h:582
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
Class to represent function types.
Type * getParamType(unsigned i) const
Parameter type accessors.
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
void addFnAttr(Attribute::AttrKind Kind)
Add function attributes to this function.
Definition Function.cpp:637
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition Function.h:168
const BasicBlock & getEntryBlock() const
Definition Function.h:793
Argument * arg_iterator
Definition Function.h:73
bool empty() const
Definition Function.h:843
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Definition Function.h:211
void removeFromParent()
removeFromParent - This method unlinks 'this' from the containing module, but does not delete it.
Definition Function.cpp:444
const DataLayout & getDataLayout() const
Get the data layout of the module this function belongs to.
Definition Function.cpp:357
Attribute getFnAttribute(Attribute::AttrKind Kind) const
Return the attribute for the given attribute kind.
Definition Function.cpp:762
DISubprogram * getSubprogram() const
Get the attached subprogram.
AttributeList getAttributes() const
Return the attribute list for this Function.
Definition Function.h:328
const Function & getFunction() const
Definition Function.h:166
iterator begin()
Definition Function.h:837
arg_iterator arg_begin()
Definition Function.h:852
void setAttributes(AttributeList Attrs)
Set the attribute list for this Function.
Definition Function.h:331
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:353
void addParamAttr(unsigned ArgNo, Attribute::AttrKind Kind)
adds the attribute to the list of attributes for the given arg.
Definition Function.cpp:665
Function::iterator insert(Function::iterator Position, BasicBlock *BB)
Insert BB in the basic block list at Position.
Definition Function.h:739
size_t arg_size() const
Definition Function.h:885
Type * getReturnType() const
Returns the type of the ret val.
Definition Function.h:216
iterator end()
Definition Function.h:839
void setCallingConv(CallingConv::ID CC)
Definition Function.h:276
Argument * getArg(unsigned i) const
Definition Function.h:870
bool hasMetadata() const
Return true if this GlobalObject has any metadata attached to it.
LLVM_ABI void addMetadata(unsigned KindID, MDNode &MD)
Add a metadata attachment.
LinkageTypes getLinkage() const
void setLinkage(LinkageTypes LT)
Module * getParent()
Get the module that this global value is contained inside of...
void setDSOLocal(bool Local)
PointerType * getType() const
Global values are always pointers.
@ HiddenVisibility
The GV is hidden.
Definition GlobalValue.h:69
@ ProtectedVisibility
The GV is protected.
Definition GlobalValue.h:70
void setVisibility(VisibilityTypes V)
LinkageTypes
An enumeration for the kinds of linkage for global values.
Definition GlobalValue.h:52
@ PrivateLinkage
Like Internal, but omit from symbol table.
Definition GlobalValue.h:61
@ CommonLinkage
Tentative definitions.
Definition GlobalValue.h:63
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
@ WeakODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:58
@ WeakAnyLinkage
Keep one copy of named function when linking (weak)
Definition GlobalValue.h:57
@ AppendingLinkage
Special purpose, only applies to global arrays.
Definition GlobalValue.h:59
@ LinkOnceODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:56
Type * getValueType() const
const Constant * getInitializer() const
getInitializer - Return the initializer for this global variable.
InsertPoint - A saved insertion point.
Definition IRBuilder.h:246
BasicBlock * getBlock() const
Definition IRBuilder.h:261
bool isSet() const
Returns true if this insert point is set.
Definition IRBuilder.h:259
BasicBlock::iterator getPoint() const
Definition IRBuilder.h:262
Common base class shared among various IRBuilders.
Definition IRBuilder.h:114
InsertPoint saveIP() const
Returns the current insert point.
Definition IRBuilder.h:266
void restoreIP(InsertPoint IP)
Sets the current insert point to a previously-saved location.
Definition IRBuilder.h:278
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2893
LLVM_ABI const DebugLoc & getStableDebugLoc() const
Fetch the debug location for this node, unless this is a debug intrinsic, in which case fetch the deb...
LLVM_ABI void removeFromParent()
This method unlinks 'this' from the containing basic block, but does not delete it.
LLVM_ABI unsigned getNumSuccessors() const LLVM_READONLY
Return the number of successors that this instruction has.
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
LLVM_ABI const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
LLVM_ABI void moveBefore(InstListType::iterator InsertPos)
Unlink this instruction from its current basic block and insert it into the basic block that MovePos ...
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
MDNode * getMetadata(unsigned KindID) const
Get the metadata of given kind attached to this Instruction.
LLVM_ABI BasicBlock * getSuccessor(unsigned Idx) const LLVM_READONLY
Return the specified successor. This instruction must be a terminator.
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set the metadata of the specified kind to the specified node.
LLVM_ABI void moveBeforePreserving(InstListType::iterator MovePos)
Perform a moveBefore operation, while signalling that the caller intends to preserve the original ord...
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
LLVM_ABI void insertAfter(Instruction *InsertPos)
Insert an unlinked instruction into a basic block immediately after the specified instruction.
Class to represent integer types.
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:348
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
An instruction for reading from memory.
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this load instruction.
Align getAlign() const
Return the alignment of the access that is being performed.
Analysis pass that exposes the LoopInfo for a function.
Definition LoopInfo.h:594
LLVM_ABI LoopInfo run(Function &F, FunctionAnalysisManager &AM)
ArrayRef< BlockT * > getBlocks() const
Get a list of the basic blocks which make up this loop.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
This class represents a loop nest and can be used to query its properties.
Represents a single loop in the control flow graph.
Definition LoopInfo.h:40
LLVM_ABI MDNode * createCallbackEncoding(unsigned CalleeArgNo, ArrayRef< int > Arguments, bool VarArgsArePassed)
Return metadata describing a callback (see llvm::AbstractCallSite).
Metadata node.
Definition Metadata.h:1069
LLVM_ABI void replaceOperandWith(unsigned I, Metadata *New)
Replace a specific operand.
static MDTuple * getDistinct(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1575
ArrayRef< MDOperand > operands() const
Definition Metadata.h:1424
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1567
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Definition Metadata.cpp:615
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:38
size_type size() const
Definition MapVector.h:58
Root of the metadata hierarchy.
Definition Metadata.h:64
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
LLVMContext & getContext() const
Get the global data context.
Definition Module.h:327
const DataLayout & getDataLayout() const
Get the data layout for the module's target platform.
Definition Module.h:320
A tuple of MDNodes.
Definition Metadata.h:1755
iterator_range< op_iterator > operands()
Definition Metadata.h:1851
LLVM_ABI void addOperand(MDNode *M)
Class that manages information about offload code regions and data.
function_ref< void(StringRef, const OffloadEntryInfoDeviceGlobalVar &)> OffloadDeviceGlobalVarEntryInfoActTy
Applies action Action on all registered entries.
OMPTargetDeviceClauseKind
Kind of device clause for declare target variables and functions NOTE: Currently not used as a part o...
@ OMPTargetDeviceClauseAny
The target is marked for all devices.
LLVM_ABI void registerDeviceGlobalVarEntryInfo(StringRef VarName, Constant *Addr, int64_t VarSize, OMPTargetGlobalVarEntryKind Flags, GlobalValue::LinkageTypes Linkage)
Register device global variable entry.
LLVM_ABI void initializeDeviceGlobalVarEntryInfo(StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order)
Initialize device global variable entry.
LLVM_ABI void actOnDeviceGlobalVarEntriesInfo(const OffloadDeviceGlobalVarEntryInfoActTy &Action)
OMPTargetRegionEntryKind
Kind of the target registry entry.
@ OMPTargetRegionEntryTargetRegion
Mark the entry as target region.
LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, const TargetRegionEntryInfo &EntryInfo)
LLVM_ABI bool hasTargetRegionEntryInfo(TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId=false) const
Return true if a target region entry with the provided information exists.
LLVM_ABI void registerTargetRegionEntryInfo(TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID, OMPTargetRegionEntryKind Flags)
Register target region entry.
LLVM_ABI void actOnTargetRegionEntriesInfo(const OffloadTargetRegionEntryInfoActTy &Action)
LLVM_ABI void initializeTargetRegionEntryInfo(const TargetRegionEntryInfo &EntryInfo, unsigned Order)
Initialize target region entry.
OMPTargetGlobalVarEntryKind
Kind of the global variable entry..
@ OMPTargetGlobalVarEntryEnter
Mark the entry as a declare target enter.
@ OMPTargetGlobalRegisterRequires
Mark the entry as a register requires global.
@ OMPTargetGlobalVarEntryIndirect
Mark the entry as a declare target indirect global.
@ OMPTargetGlobalVarEntryLink
Mark the entry as a to declare target link.
@ OMPTargetGlobalVarEntryTo
Mark the entry as a to declare target.
@ OMPTargetGlobalVarEntryIndirectVTable
Mark the entry as a declare target indirect vtable.
function_ref< void(const TargetRegionEntryInfo &EntryInfo, const OffloadEntryInfoTargetRegion &)> OffloadTargetRegionEntryInfoActTy
brief Applies action Action on all registered entries.
bool hasDeviceGlobalVarEntryInfo(StringRef VarName) const
Checks if the variable with the given name has been registered already.
LLVM_ABI bool empty() const
Return true if a there are no entries defined.
std::optional< bool > IsTargetDevice
Flag to define whether to generate code for the role of the OpenMP host (if set to false) or device (...
std::optional< bool > IsGPU
Flag for specifying if the compilation is done for an accelerator.
LLVM_ABI int64_t getRequiresFlags() const
Returns requires directive clauses as flags compatible with those expected by libomptarget.
std::optional< bool > OpenMPOffloadMandatory
Flag for specifying if offloading is mandatory.
LLVM_ABI void setHasRequiresReverseOffload(bool Value)
LLVM_ABI bool hasRequiresUnifiedSharedMemory() const
LLVM_ABI void setHasRequiresUnifiedSharedMemory(bool Value)
unsigned getDefaultTargetAS() const
LLVM_ABI bool hasRequiresDynamicAllocators() const
LLVM_ABI void setHasRequiresUnifiedAddress(bool Value)
LLVM_ABI void setHasRequiresDynamicAllocators(bool Value)
LLVM_ABI bool hasRequiresReverseOffload() const
LLVM_ABI bool hasRequiresUnifiedAddress() const
Struct that keeps the information that should be kept throughout a 'target data' region.
An interface to create LLVM-IR for OpenMP directives.
LLVM_ABI InsertPointOrErrorTy createOrderedThreadsSimd(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsThreads)
Generator for 'omp ordered [threads | simd]'.
LLVM_ABI void emitAArch64DeclareSimdFunction(llvm::Function *Fn, unsigned VLENVal, llvm::ArrayRef< DeclareSimdAttrTy > ParamAttrs, DeclareSimdBranch Branch, char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput)
Emit AArch64 vector-function ABI attributes for a declare simd function.
LLVM_ABI Constant * getOrCreateIdent(Constant *SrcLocStr, uint32_t SrcLocStrSize, omp::IdentFlag Flags=omp::IdentFlag(0), unsigned Reserve2Flags=0)
Return an ident_t* encoding the source location SrcLocStr and Flags.
LLVM_ABI void registerDeclareTargetGlobalReplacement(GlobalValue *Original, GlobalValue *Replacement)
Register a module-scope replacement of a declare target global variable.
LLVM_ABI FunctionCallee getOrCreateRuntimeFunction(Module &M, omp::RuntimeFunction FnID)
Return the function declaration for the runtime function with FnID.
LLVM_ABI InsertPointOrErrorTy createCancel(const LocationDescription &Loc, Value *IfCondition, omp::Directive CanceledDirective)
Generator for 'omp cancel'.
std::function< Expected< Function * >(StringRef FunctionName)> FunctionGenCallback
Functions used to generate a function with the given name.
LLVM_ABI CallInst * createOMPAllocShared(const LocationDescription &Loc, Value *Size, const Twine &Name=Twine(""))
Create a runtime call for kmpc_alloc_shared.
ReductionGenCBKind
Enum class for the RedctionGen CallBack type to be used.
LLVM_ABI CanonicalLoopInfo * collapseLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops, InsertPointTy ComputeIP)
Collapse a loop nest into a single loop.
LLVM_ABI void createTaskyield(const LocationDescription &Loc)
Generator for 'omp taskyield'.
std::function< Error(InsertPointTy CodeGenIP)> FinalizeCallbackTy
Callback type for variable finalization (think destructors).
LLVM_ABI void emitBranch(BasicBlock *Target)
LLVM_ABI Error emitCancelationCheckImpl(Value *CancelFlag, omp::Directive CanceledDirective)
Generate control flow and cleanup for cancellation.
static LLVM_ABI void writeThreadBoundsForKernel(const Triple &T, Function &Kernel, int32_t LB, int32_t UB)
LLVM_ABI void emitTaskwaitImpl(const LocationDescription &Loc)
Generate a taskwait runtime call.
LLVM_ABI Constant * registerTargetRegionFunction(TargetRegionEntryInfo &EntryInfo, Function *OutlinedFunction, StringRef EntryFnName, StringRef EntryFnIDName)
Registers the given function and sets up the attribtues of the function Returns the FunctionID.
LLVM_ABI GlobalVariable * emitKernelExecutionMode(StringRef KernelName, omp::OMPTgtExecModeFlags Mode)
Emit the kernel execution mode.
LLVM_ABI void initialize()
Initialize the internal state, this will put structures types and potentially other helpers into the ...
LLVM_ABI InsertPointTy createAtomicCompare(const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V, AtomicOpValue &R, Value *E, Value *D, AtomicOrdering AO, omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate, bool IsFailOnly, bool IsWeak=false)
LLVM_ABI InsertPointTy createAtomicWrite(const LocationDescription &Loc, AtomicOpValue &X, Value *Expr, AtomicOrdering AO, InsertPointTy AllocaIP)
Emit atomic write for : X = Expr — Only Scalar data types.
LLVM_ABI void loadOffloadInfoMetadata(Module &M)
Loads all the offload entries information from the host IR metadata.
function_ref< MapInfosTy &(InsertPointTy CodeGenIP)> GenMapInfoCallbackTy
Callback type for creating the map infos for the kernel parameters.
LLVM_ABI Error emitOffloadingArrays(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo, TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous=false, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr)
Emit the arrays used to pass the captures and map information to the offloading runtime library.
LLVM_ABI void unrollLoopFull(DebugLoc DL, CanonicalLoopInfo *Loop)
Fully unroll a loop.
function_ref< Error(InsertPointTy CodeGenIP, Value *IndVar)> LoopBodyGenCallbackTy
Callback type for loop body code generation.
LLVM_ABI InsertPointOrErrorTy emitScanReduction(const LocationDescription &Loc, ArrayRef< llvm::OpenMPIRBuilder::ReductionInfo > ReductionInfos, ScanInfo *ScanRedInfo)
This function performs the scan reduction of the values updated in the input phase.
LLVM_ABI void emitFlush(const LocationDescription &Loc)
Generate a flush runtime call.
LLVM_ABI InsertPointOrErrorTy createScope(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsNowait)
Generator for 'omp scope'.
static LLVM_ABI std::pair< int32_t, int32_t > readThreadBoundsForKernel(const Triple &T, Function &Kernel)
}
OpenMPIRBuilderConfig Config
The OpenMPIRBuilder Configuration.
LLVM_ABI CallInst * createOMPInteropDestroy(const LocationDescription &Loc, Value *InteropVar, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_destroy.
LLVM_ABI void emitUsed(StringRef Name, ArrayRef< llvm::WeakTrackingVH > List)
Emit the llvm.used metadata.
LLVM_ABI InsertPointOrErrorTy createSingle(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef< llvm::Value * > CPVars={}, ArrayRef< llvm::Function * > CPFuncs={})
Generator for 'omp single'.
LLVM_ABI InsertPointOrErrorTy createTeams(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower=nullptr, Value *NumTeamsUpper=nullptr, Value *ThreadLimit=nullptr, Value *IfExpr=nullptr)
Generator for #omp teams
std::forward_list< CanonicalLoopInfo > LoopInfos
Collection of owned canonical loop objects that eventually need to be free'd.
LLVM_ABI llvm::StructType * getKmpTaskAffinityInfoTy()
Return the LLVM struct type matching runtime kmp_task_affinity_info_t.
LLVM_ABI std::string createPlatformSpecificName(ArrayRef< StringRef > Parts) const
Get the create a name using the platform specific separators.
LLVM_ABI FunctionCallee createDispatchNextFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_next_* runtime function for the specified size IVSize and sign IVSigned.
static LLVM_ABI void getKernelArgsVector(TargetKernelArgs &KernelArgs, IRBuilderBase &Builder, SmallVector< Value * > &ArgsVector)
Create the kernel args vector used by emitTargetKernel.
LLVM_ABI InsertPointOrErrorTy createTarget(const LocationDescription &Loc, bool IsOffloadEntry, OpenMPIRBuilder::InsertPointTy AllocaIP, OpenMPIRBuilder::InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks, TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo, const TargetKernelDefaultAttrs &DefaultAttrs, const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond, SmallVectorImpl< Value * > &Inputs, GenMapInfoCallbackTy GenMapInfoCB, TargetBodyGenCallbackTy BodyGenCB, TargetGenArgAccessorsCallbackTy ArgAccessorFuncCB, CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies={}, bool HasNowait=false, Value *DynCGroupMem=nullptr, omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback=omp::OMPDynGroupprivateFallbackType::Abort)
Generator for 'omp target'.
LLVM_ABI void unrollLoopHeuristic(DebugLoc DL, CanonicalLoopInfo *Loop)
Fully or partially unroll a loop.
LLVM_ABI omp::OpenMPOffloadMappingFlags getMemberOfFlag(unsigned Position)
Get OMP_MAP_MEMBER_OF flag with extra bits reserved based on the position given.
LLVM_ABI void addAttributes(omp::RuntimeFunction FnID, Function &Fn)
Add attributes known for FnID to Fn.
Module & M
The underlying LLVM-IR module.
StringMap< Constant * > SrcLocStrMap
Map to remember source location strings.
LLVM_ABI void createMapperAllocas(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumOperands, struct MapperAllocas &MapperAllocas)
Create the allocas instruction used in call to mapper functions.
SmallVector< DeclareTargetGlobalReplacement, 8 > DeclareTargetGlobalReplacements
Collection of declare target globals to rewrite uses of during device module finalizaiton.
LLVM_ABI Constant * getOrCreateSrcLocStr(StringRef LocStr, uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the source location LocStr.
LLVM_ABI Error emitTargetRegionFunction(TargetRegionEntryInfo &EntryInfo, FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry, Function *&OutlinedFn, Constant *&OutlinedFnID)
Create a unique name for the entry function using the source location information of the current targ...
LLVM_ABI InsertPointOrErrorTy createIteratorLoop(LocationDescription Loc, llvm::Value *TripCount, IteratorBodyGenTy BodyGen, llvm::StringRef Name="iterator")
Create a canonical iterator loop at the current insertion point.
LLVM_ABI Expected< SmallVector< llvm::CanonicalLoopInfo * > > createCanonicalScanLoops(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo)
Generator for the control flow structure of an OpenMP canonical loops if the parent directive has an ...
LLVM_ABI FunctionCallee createDispatchFiniFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_fini_* runtime function for the specified size IVSize and sign IVSigned.
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> TargetBodyGenCallbackTy
LLVM_ABI void unrollLoopPartial(DebugLoc DL, CanonicalLoopInfo *Loop, int32_t Factor, CanonicalLoopInfo **UnrolledCLI)
Partially unroll a loop.
function_ref< Error(Value *DeviceID, Value *RTLoc, IRBuilderBase::InsertPoint TargetTaskAllocaIP)> TargetTaskBodyCallbackTy
Callback type for generating the bodies of device directives that require outer target tasks (e....
Expected< MapInfosTy & > MapInfosOrErrorTy
bool HandleFPNegZero
Emit atomic compare for constructs: — Only scalar data types cond-expr-stmt: x = x ordop expr ?
LLVM_ABI void emitTaskyieldImpl(const LocationDescription &Loc)
Generate a taskyield runtime call.
LLVM_ABI void emitMapperCall(const LocationDescription &Loc, Function *MapperFunc, Value *SrcLocInfo, Value *MaptypesArg, Value *MapnamesArg, struct MapperAllocas &MapperAllocas, int64_t DeviceID, unsigned NumOperands)
Create the call for the target mapper function.
LLVM_ABI InsertPointOrErrorTy createDistribute(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB)
Generator for #omp distribute
LLVM_ABI InsertPointOrErrorTy createTask(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB, bool Tied=true, Value *Final=nullptr, Value *IfCondition=nullptr, const DependenciesInfo &Dependencies={}, const AffinityData &Affinities={}, bool Mergeable=false, Value *EventHandle=nullptr, Value *Priority=nullptr)
Generator for #omp taskloop
function_ref< Expected< Function * >(unsigned int)> CustomMapperCallbackTy
LLVM_ABI InsertPointTy createOrderedDepend(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumLoops, ArrayRef< llvm::Value * > StoreValues, const Twine &Name, bool IsDependSource)
Generator for 'omp ordered depend (source | sink)'.
LLVM_ABI InsertPointTy createCopyinClauseBlocks(InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr, llvm::IntegerType *IntPtrTy, bool BranchtoEnd=true)
Generate conditional branch and relevant BasicBlocks through which private threads copy the 'copyin' ...
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &Original, Value &Inner, Value *&ReplVal)> PrivatizeCallbackTy
Callback type for variable privatization (think copy & default constructor).
LLVM_ABI bool isFinalized()
Check whether the finalize function has already run.
SmallVector< FinalizationInfo, 8 > FinalizationStack
The finalization stack made up of finalize callbacks currently in-flight, wrapped into FinalizationIn...
LLVM_ABI std::vector< CanonicalLoopInfo * > tileLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops, ArrayRef< Value * > TileSizes)
Tile a loop nest.
LLVM_ABI CallInst * createOMPInteropInit(const LocationDescription &Loc, Value *InteropVar, omp::OMPInteropType InteropType, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_init.
LLVM_ABI Error emitIfClause(Value *Cond, BodyGenCallbackTy ThenGen, BodyGenCallbackTy ElseGen, InsertPointTy AllocaIP={}, ArrayRef< BasicBlock * > DeallocBlocks={})
Emits code for OpenMP 'if' clause using specified BodyGenCallbackTy Here is the logic: if (Cond) { Th...
LLVM_ABI void finalize(Function *Fn=nullptr)
Finalize the underlying module, e.g., by outlining regions.
LLVM_ABI Function * getOrCreateRuntimeFunctionPtr(omp::RuntimeFunction FnID)
void addOutlineInfo(std::unique_ptr< OutlineInfo > &&OI)
Add a new region that will be outlined later.
LLVM_ABI InsertPointTy createTargetInit(const LocationDescription &Loc, const llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs &Attrs)
The omp target interface.
LLVM_ABI InsertPointOrErrorTy createReductions(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< ReductionInfo > ReductionInfos, ArrayRef< bool > IsByRef, bool IsNoWait=false, bool IsTeamsReduction=false)
Generator for 'omp reduction'.
const Triple T
The target triple of the underlying module.
DenseMap< std::pair< Constant *, uint64_t >, Constant * > IdentMap
Map to remember existing ident_t*.
LLVM_ABI CallInst * createOMPFree(const LocationDescription &Loc, Value *Addr, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_free.
LLVM_ABI InsertPointOrErrorTy createReductionsGPU(const LocationDescription &Loc, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< ReductionInfo > ReductionInfos, ArrayRef< bool > IsByRef, bool IsNoWait=false, bool IsTeamsReduction=false, bool IsSPMD=false, ReductionGenCBKind ReductionGenCBKind=ReductionGenCBKind::MLIR, std::optional< omp::GV > GridValue={}, Value *SrcLocInfo=nullptr)
Design of OpenMP reductions on the GPU.
LLVM_ABI FunctionCallee createForStaticInitFunction(unsigned IVSize, bool IVSigned, bool IsGPUDistribute)
Returns __kmpc_for_static_init_* runtime function for the specified size IVSize and sign IVSigned.
LLVM_ABI CallInst * createOMPAlloc(const LocationDescription &Loc, Value *Size, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_alloc.
LLVM_ABI void emitNonContiguousDescriptor(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo, TargetDataInfo &Info)
Emit an array of struct descriptors to be assigned to the offload args.
LLVM_ABI InsertPointOrErrorTy createSection(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB)
Generator for 'omp section'.
LLVM_ABI InsertPointOrErrorTy createTaskgroup(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB)
Generator for the taskgroup construct.
LLVM_ABI InsertPointOrErrorTy createParallel(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB, PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition, Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable)
Generator for 'omp parallel'.
function_ref< InsertPointOrErrorTy(InsertPointTy)> EmitFallbackCallbackTy
Callback function type for functions emitting the host fallback code that is executed when the kernel...
static LLVM_ABI TargetRegionEntryInfo getTargetEntryUniqueInfo(FileIdentifierInfoCallbackTy CallBack, vfs::FileSystem &VFS, StringRef ParentName="")
Creates a unique info for a target entry when provided a filename and line number from.
LLVM_ABI void emitTaskDependency(IRBuilderBase &Builder, Value *Entry, const DependData &Dep)
Store one kmp_depend_info entry at the given Entry pointer.
LLVM_ABI void emitBlock(BasicBlock *BB, Function *CurFn, bool IsFinished=false)
LLVM_ABI Value * getOrCreateThreadID(Value *Ident)
Return the current thread ID.
LLVM_ABI InsertPointOrErrorTy createMaster(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB)
Generator for 'omp master'.
LLVM_ABI InsertPointOrErrorTy createTargetData(const LocationDescription &Loc, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks, Value *DeviceID, Value *IfCond, TargetDataInfo &Info, GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB, omp::RuntimeFunction *MapperFunc=nullptr, function_ref< InsertPointOrErrorTy(InsertPointTy CodeGenIP, BodyGenTy BodyGenType)> BodyGenCB=nullptr, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr, Value *SrcLocInfo=nullptr)
Generator for 'omp target data'.
LLVM_ABI CallInst * createRuntimeFunctionCall(FunctionCallee Callee, ArrayRef< Value * > Args, StringRef Name="")
LLVM_ABI InsertPointOrErrorTy emitKernelLaunch(const LocationDescription &Loc, Value *OutlinedFnID, EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args, Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP)
Generate a target region entry call and host fallback call.
StringMap< GlobalVariable *, BumpPtrAllocator > InternalVars
An ordered map of auto-generated variables to their unique names.
LLVM_ABI InsertPointOrErrorTy createCancellationPoint(const LocationDescription &Loc, omp::Directive CanceledDirective)
Generator for 'omp cancellation point'.
LLVM_ABI CallInst * createOMPAlignedAlloc(const LocationDescription &Loc, Value *Align, Value *Size, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_align_alloc.
LLVM_ABI FunctionCallee createDispatchInitFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_init_* runtime function for the specified size IVSize and sign IVSigned.
LLVM_ABI InsertPointOrErrorTy createScan(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< llvm::Value * > ScanVars, ArrayRef< llvm::Type * > ScanVarsType, bool IsInclusive, ScanInfo *ScanRedInfo)
This directive split and directs the control flow to input phase blocks or scan phase blocks based on...
LLVM_ABI CallInst * createOMPFreeShared(const LocationDescription &Loc, Value *Addr, Value *Size, const Twine &Name=Twine(""))
Create a runtime call for kmpc_free_shared.
LLVM_ABI CallInst * createOMPInteropUse(const LocationDescription &Loc, Value *InteropVar, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_use.
IRBuilder<>::InsertPoint InsertPointTy
Type used throughout for insertion points.
LLVM_ABI GlobalVariable * getOrCreateInternalVariable(Type *Ty, const StringRef &Name, std::optional< unsigned > AddressSpace={})
Gets (if variable with the given name already exist) or creates internal global variable with the spe...
LLVM_ABI GlobalVariable * createOffloadMapnames(SmallVectorImpl< llvm::Constant * > &Names, std::string VarName)
Create the global variable holding the offload names information.
std::forward_list< ScanInfo > ScanInfos
Collection of owned ScanInfo objects that eventually need to be free'd.
static LLVM_ABI void writeTeamsForKernel(const Triple &T, Function &Kernel, int32_t LB, int32_t UB)
LLVM_ABI Value * calculateCanonicalLoopTripCount(const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, const Twine &Name="loop")
Calculate the trip count of a canonical loop.
LLVM_ABI InsertPointOrErrorTy createBarrier(const LocationDescription &Loc, omp::Directive Kind, bool ForceSimpleCall=false, bool CheckCancelFlag=true)
Emitter methods for OpenMP directives.
LLVM_ABI void setCorrectMemberOfFlag(omp::OpenMPOffloadMappingFlags &Flags, omp::OpenMPOffloadMappingFlags MemberOfFlag)
Given an initial flag set, this function modifies it to contain the passed in MemberOfFlag generated ...
LLVM_ABI Error emitOffloadingArraysAndArgs(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info, TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo, CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous=false, bool ForEndCall=false, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr)
Allocates memory for and populates the arrays required for offloading (offload_{baseptrs|ptrs|mappers...
LLVM_ABI Constant * getOrCreateDefaultSrcLocStr(uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the default source location.
LLVM_ABI InsertPointOrErrorTy createCritical(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst)
Generator for 'omp critical'.
LLVM_ABI void createError(const LocationDescription &Loc, bool IsFatal, Value *Message)
Generate a call to the runtime to emit the diagnostic of an OpenMP error directive with at(execution)...
LLVM_ABI void createOffloadEntry(Constant *ID, Constant *Addr, uint64_t Size, int32_t Flags, GlobalValue::LinkageTypes, StringRef Name="")
Creates offloading entry for the provided entry ID ID, address Addr, size Size, and flags Flags.
static LLVM_ABI unsigned getOpenMPDefaultSimdAlign(const Triple &TargetTriple, const StringMap< bool > &Features)
Get the default alignment value for given target.
LLVM_ABI unsigned getFlagMemberOffset()
Get the offset of the OMP_MAP_MEMBER_OF field.
LLVM_ABI InsertPointOrErrorTy applyWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP, bool NeedsBarrier, llvm::omp::ScheduleKind SchedKind=llvm::omp::OMP_SCHEDULE_Default, Value *ChunkSize=nullptr, bool HasSimdModifier=false, bool HasMonotonicModifier=false, bool HasNonmonotonicModifier=false, bool HasOrderedClause=false, omp::WorksharingLoopType LoopType=omp::WorksharingLoopType::ForStaticLoop, bool NoLoop=false, bool HasDistSchedule=false, Value *DistScheduleChunkSize=nullptr)
Modifies the canonical loop to be a workshare loop.
LLVM_ABI InsertPointOrErrorTy createAtomicCapture(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, AtomicOpValue &V, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: — Only Scalar data types V = X; X = X BinOp Expr ,...
LLVM_ABI CanonicalLoopInfo * createLoopSkeleton(DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore, BasicBlock *PostInsertBefore, const Twine &Name={}, bool IsCollapsed=false)
Create the control flow structure of a canonical OpenMP loop.
LLVM_ABI void createOffloadEntriesAndInfoMetadata(EmitMetadataErrorReportFunctionTy &ErrorReportFunction)
LLVM_ABI void applySimd(CanonicalLoopInfo *Loop, MapVector< Value *, Value * > AlignedVars, Value *IfCond, omp::OrderKind Order, ConstantInt *Simdlen, ConstantInt *Safelen)
Add metadata to simd-ize a loop.
SmallVector< std::unique_ptr< OutlineInfo >, 16 > OutlineInfos
Collection of regions that need to be outlined during finalization.
LLVM_ABI InsertPointOrErrorTy createAtomicUpdate(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: X = X BinOp Expr ,or X = Expr BinOp X For complex Operations: X = ...
std::function< std::tuple< std::string, uint64_t >()> FileIdentifierInfoCallbackTy
bool isLastFinalizationInfoCancellable(omp::Directive DK)
Return true if the last entry in the finalization stack is of kind DK and cancellable.
LLVM_ABI InsertPointTy emitTargetKernel(const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return, Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads, Value *HostPtr, ArrayRef< Value * > KernelArgs)
Generate a target region entry call.
LLVM_ABI GlobalVariable * createOffloadMaptypes(SmallVectorImpl< uint64_t > &Mappings, std::string VarName)
Create the global variable holding the offload mappings information.
LLVM_ABI Expected< Function * > emitUserDefinedMapper(function_ref< MapInfosOrErrorTy(InsertPointTy CodeGenIP, llvm::Value *PtrPHI, llvm::Value *BeginArg)> PrivAndGenMapInfoCB, llvm::Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB, bool PreserveMemberOfFlags=false, bool PropagatePresentToPointee=false)
Emit the user-defined mapper function.
LLVM_ABI CallInst * createCachedThreadPrivate(const LocationDescription &Loc, llvm::Value *Pointer, llvm::ConstantInt *Size, const llvm::Twine &Name=Twine(""))
Create a runtime call for kmpc_threadprivate_cached.
IRBuilder Builder
The LLVM-IR Builder used to create IR.
LLVM_ABI GlobalValue * createGlobalFlag(unsigned Value, StringRef Name)
Create a hidden global flag Name in the module with initial value Value.
LLVM_ABI void emitOffloadingArraysArgument(IRBuilderBase &Builder, OpenMPIRBuilder::TargetDataRTArgs &RTArgs, OpenMPIRBuilder::TargetDataInfo &Info, bool ForEndCall=false)
Emit the arguments to be passed to the runtime library based on the arrays of base pointers,...
LLVM_ABI InsertPointOrErrorTy createMasked(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, Value *Filter)
Generator for 'omp masked'.
LLVM_ABI Expected< CanonicalLoopInfo * > createCanonicalLoop(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *TripCount, const Twine &Name="loop")
Generator for the control flow structure of an OpenMP canonical loop.
function_ref< Expected< InsertPointTy >( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value *DestPtr, Value *SrcPtr)> TaskDupCallbackTy
Callback type for task duplication function code generation.
LLVM_ABI Value * getSizeInBytes(Value *BasePtr)
Computes the size of type in bytes.
llvm::function_ref< llvm::Error( InsertPointTy BodyIP, llvm::Value *LinearIV)> IteratorBodyGenTy
LLVM_ABI FunctionCallee createDispatchDeinitFunction()
Returns __kmpc_dispatch_deinit runtime function.
LLVM_ABI void registerTargetGlobalVariable(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy, Constant *Addr)
Registers a target variable for device or host.
LLVM_ABI void createTargetDeinit(const LocationDescription &Loc, int32_t TeamsReductionDataSize=0)
Create a runtime call for kmpc_target_deinit.
BodyGenTy
Type of BodyGen to use for region codegen.
LLVM_ABI CanonicalLoopInfo * fuseLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops)
Fuse a sequence of loops.
LLVM_ABI void emitX86DeclareSimdFunction(llvm::Function *Fn, unsigned NumElements, const llvm::APSInt &VLENVal, llvm::ArrayRef< DeclareSimdAttrTy > ParamAttrs, DeclareSimdBranch Branch)
Emit x86 vector-function ABI attributes for a declare simd function.
SmallVector< llvm::Function *, 16 > ConstantAllocaRaiseCandidates
A collection of candidate target functions that's constant allocas will attempt to be raised on a cal...
OffloadEntriesInfoManager OffloadInfoManager
Info manager to keep track of target regions.
static LLVM_ABI std::pair< int32_t, int32_t > readTeamBoundsForKernel(const Triple &T, Function &Kernel)
Read/write a bounds on teams for Kernel.
const std::string ompOffloadInfoName
OMP Offload Info Metadata name string.
Expected< InsertPointTy > InsertPointOrErrorTy
Type used to represent an insertion point or an error value.
LLVM_ABI InsertPointTy createCopyPrivate(const LocationDescription &Loc, llvm::Value *BufSize, llvm::Value *CpyBuf, llvm::Value *CpyFn, llvm::Value *DidIt)
Generator for __kmpc_copyprivate.
LLVM_ABI InsertPointOrErrorTy createSections(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< StorableBodyGenCallbackTy > SectionCBs, PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait)
Generator for 'omp sections'.
std::function< void(EmitMetadataErrorKind, TargetRegionEntryInfo)> EmitMetadataErrorReportFunctionTy
Callback function type.
function_ref< InsertPointOrErrorTy( Argument &Arg, Value *Input, Value *&RetVal, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< InsertPointTy > DeallocIPs)> TargetGenArgAccessorsCallbackTy
LLVM_ABI Expected< ScanInfo * > scanInfoInitialize()
Creates a ScanInfo object, allocates and returns the pointer.
LLVM_ABI InsertPointOrErrorTy emitTargetTask(TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc, OpenMPIRBuilder::InsertPointTy AllocaIP, const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs, bool HasNoWait)
Generate a target-task for the target construct.
LLVM_ABI InsertPointTy createAtomicRead(const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V, AtomicOrdering AO, InsertPointTy AllocaIP)
Emit atomic Read for : V = X — Only Scalar data types.
function_ref< Error(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> BodyGenCallbackTy
Callback type for body (=inner region) code generation.
bool updateToLocation(const LocationDescription &Loc)
Update the internal location to Loc.
LLVM_ABI void createFlush(const LocationDescription &Loc)
Generator for 'omp flush'.
LLVM_ABI void createTaskwait(const LocationDescription &Loc, DependenciesInfo Dependencies={})
Generator for 'omp taskwait'.
LLVM_ABI Constant * getAddrOfDeclareTargetVar(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, Type *LlvmPtrTy, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage)
Retrieve (or create if non-existent) the address of a declare target variable, used in conjunction wi...
origPtr *with the address space normalization required by the runtime entry point *The NULL descriptor makes the runtime walk the enclosing taskgroups to *find the matching task_reduction registration for the item The lookups *are emitted at p Loc
EmitMetadataErrorKind
The kind of errors that can occur when emitting the offload entries and metadata.
unsigned getOpcode() const
Return the opcode for this Instruction or ConstantExpr.
Definition Operator.h:43
The optimization diagnostic interface.
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
Pseudo-analysis pass that exposes the PassInstrumentation to pass managers.
Class to represent pointers.
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
static LLVM_ABI PointerType * get(LLVMContext &C, unsigned AddressSpace)
This constructs an opaque pointer to an object in a numbered address space.
Definition Type.cpp:911
PostDominatorTree Class - Concrete subclass of DominatorTree that is used to compute the post-dominat...
Analysis pass that exposes the ScalarEvolution for a function.
LLVM_ABI ScalarEvolution run(Function &F, FunctionAnalysisManager &AM)
The main scalar evolution driver.
ScanInfo holds the information to assist in lowering of Scan reduction.
llvm::SmallDenseMap< llvm::Value *, llvm::Value * > * ScanBuffPtrs
Maps the private reduction variable to the pointer of the temporary buffer.
llvm::BasicBlock * OMPScanLoopExit
Exit block of loop body.
llvm::Value * IV
Keeps track of value of iteration variable for input/scan loop to be used for Scan directive lowering...
llvm::BasicBlock * OMPAfterScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanInit
Block before loop body where scan initializations are done.
llvm::BasicBlock * OMPBeforeScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanFinish
Block after loop body where scan finalizations are done.
llvm::Value * Span
Stores the span of canonical loop being lowered to be used for temporary buffer allocation or Finaliz...
bool OMPFirstScanLoop
If true, it indicates Input phase is lowered; else it indicates ScanPhase is lowered.
llvm::BasicBlock * OMPScanDispatch
Controls the flow to before or after scan blocks.
A vector that has set insertion semantics.
Definition SetVector.h:57
bool remove_if(UnaryPredicate P)
Remove items from the set vector based on a predicate function.
Definition SetVector.h:236
bool empty() const
Determine if the SetVector is empty or not.
Definition SetVector.h:100
This is a 'bitvector' (really, a variable-sized bit array), optimized for the case when the array is ...
SmallBitVector & set()
bool test(unsigned Idx) const
Returns true if bit Idx is set.
bool all() const
Returns true if all bits are set.
bool any() const
Returns true if any bit is set.
A templated base class for SmallPtrSet which provides the typesafe interface that is common across al...
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition SmallSet.h:134
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
void append(StringRef RHS)
Append from a StringRef.
Definition SmallString.h:68
StringRef str() const
Explicit conversion to StringRef.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
void reserve(size_type N)
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void resize(size_type N)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
void setAlignment(Align Align)
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this store instruction.
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:128
ValueTy lookup(StringRef Key) const
lookup - Return the entry for the specified key, or a default constructed value if no such entry exis...
Definition StringMap.h:249
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
std::string str() const
Get the contents as an std::string.
Definition StringRef.h:222
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
size_t count(char C) const
Return the number of occurrences of C in the string.
Definition StringRef.h:471
bool ends_with(StringRef Suffix) const
Check if this string ends with the given Suffix.
Definition StringRef.h:270
StringRef drop_back(size_t N=1) const
Return a StringRef equal to 'this' but with the last N elements dropped.
Definition StringRef.h:642
Class to represent struct types.
static LLVM_ABI StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition Type.cpp:477
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:683
Type * getElementType(unsigned N) const
Multiway switch.
LLVM_ABI void addCase(ConstantInt *OnVal, BasicBlock *Dest)
Add an entry to the switch instruction.
Analysis pass providing the TargetTransformInfo.
LLVM_ABI Result run(const Function &F, FunctionAnalysisManager &)
Analysis pass providing the TargetLibraryInfo.
Target - Wrapper for Target specific information.
TargetMachine * createTargetMachine(const Triple &TT, StringRef CPU, StringRef Features, const TargetOptions &Options, std::optional< Reloc::Model > RM, std::optional< CodeModel::Model > CM=std::nullopt, CodeGenOptLevel OL=CodeGenOptLevel::Default, bool JIT=false) const
createTargetMachine - Create a target specific machine implementation for the specified Triple.
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
bool isPPC() const
Tests whether the target is PowerPC (32- or 64-bit LE or BE).
Definition Triple.h:1135
bool isX86() const
Tests whether the target is x86 (32- or 64-bit).
Definition Triple.h:1195
bool isWasm() const
Tests whether the target is wasm (32- and 64-bit).
Definition Triple.h:1209
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
LLVM_ABI std::string str() const
Return the twine contents as a std::string.
Definition Twine.cpp:17
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt64Ty(LLVMContext &C)
Definition Type.cpp:310
LLVM_ABI unsigned getIntegerBitWidth() const
LLVM_ABI Type * getStructElementType(unsigned N) const
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:309
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:282
LLVM_ABI unsigned getPointerAddressSpace() const
Get the address space of this pointer or pointer vector type.
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:282
bool isStructTy() const
True if this is an instance of StructType.
Definition Type.h:276
LLVM_ABI unsigned getScalarSizeInBits() const LLVM_READONLY
If this is a vector type, return the getPrimitiveSizeInBits value for the element type.
Definition Type.cpp:232
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition Type.h:186
bool isIntegerTy() const
True if this is an instance of IntegerType.
Definition Type.h:257
static LLVM_ABI IntegerType * getIntNTy(LLVMContext &C, unsigned N)
Definition Type.cpp:313
bool isVoidTy() const
Return true if this is 'void'.
Definition Type.h:141
Unconditional Branch instruction.
static UncondBrInst * Create(BasicBlock *Target, InsertPosition InsertBefore=nullptr)
static LLVM_ABI UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
This function has undefined behavior.
Produce an estimate of the unrolled cost of the specified loop.
Definition UnrollLoop.h:150
LLVM_ABI bool canUnroll(OptimizationRemarkEmitter *ORE=nullptr, const Loop *L=nullptr) const
Whether it is legal to unroll this loop.
uint64_t getRolledLoopSize() const
Definition UnrollLoop.h:174
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
void setOperand(unsigned i, Value *Val)
Definition User.h:212
Value * getOperand(unsigned i) const
Definition User.h:207
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
user_iterator user_begin()
Definition Value.h:402
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
Definition Value.cpp:394
bool hasOneUse() const
Return true if there is exactly one use of this value.
Definition Value.h:439
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
iterator_range< user_iterator > users()
Definition Value.h:426
User * user_back()
Definition Value.h:412
LLVM_ABI Align getPointerAlignment(const DataLayout &DL) const
Returns an alignment of the pointer value.
Definition Value.cpp:1002
LLVM_ABI bool hasNUses(unsigned N) const
Return true if this Value has exactly N uses.
Definition Value.cpp:147
LLVM_ABI User * getUniqueUndroppableUser()
Return true if there is exactly one unique user of this value that cannot be dropped (that user can h...
Definition Value.cpp:185
LLVM_ABI const Value * stripPointerCasts() const
Strip off pointer casts, all-zero GEPs and address space casts.
Definition Value.cpp:713
bool use_empty() const
Definition Value.h:346
user_iterator user_end()
Definition Value.h:410
LLVM_ABI bool replaceUsesWithIf(Value *New, llvm::function_ref< bool(Use &U)> ShouldReplace)
Go through the uses list for this definition and make each use point to "V" if the callback ShouldRep...
Definition Value.cpp:561
iterator_range< use_iterator > uses()
Definition Value.h:380
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:319
An efficient, type-erasing, non-owning reference to a callable.
const ParentTy * getParent() const
Definition ilist_node.h:34
self_iterator getIterator()
Definition ilist_node.h:123
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition ilist_node.h:348
A raw_ostream that writes to an SmallVector or SmallString.
StringRef str() const
Return a StringRef for the vector contents.
The virtual file system interface.
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(const Twine &Name, int64_t FileSize=-1, bool RequiresNullTerminator=true, bool IsVolatile=false, bool IsText=true)
This is a convenience method that opens a file, gets its content and then closes the file.
virtual llvm::ErrorOr< Status > status(const Twine &Path)=0
Get the status of the entry at Path, if one exists.
CallInst * Call
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
@ AMDGPU_KERNEL
Used for AMDGPU code object kernels.
@ SPIR_KERNEL
Used for SPIR kernel functions.
@ PTX_Kernel
Call to a PTX kernel. Passes all arguments in parameter space.
@ BasicBlock
Various leaf nodes.
Definition ISDOpcodes.h:81
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
Flag
These should be considered private to the implementation of the MCInstrDesc class.
constexpr StringLiteral MaxNTID("nvvm.maxntid")
constexpr StringLiteral MaxClusterRank("nvvm.maxclusterrank")
initializer< Ty > init(const Ty &Val)
@ User
could "use" a pointer
LLVM_ABI GlobalVariable * emitOffloadingEntry(Module &M, object::OffloadKind Kind, Constant *Addr, StringRef Name, uint64_t Size, uint32_t Flags, uint64_t Data, Constant *AuxAddr=nullptr)
Definition Utility.cpp:105
OpenMPOffloadMappingFlags
Values for bit flags used to specify the mapping type for offloading.
@ OMP_MAP_PTR_AND_OBJ
The element being mapped is a pointer-pointee pair; both the pointer and the pointee should be mapped...
@ OMP_MAP_MEMBER_OF
The 16 MSBs of the flags indicate whether the entry is member of some struct/class.
IdentFlag
IDs for all omp runtime library ident_t flag encodings (see their defintion in openmp/runtime/src/kmp...
RuntimeFunction
IDs for all omp runtime library (RTL) functions.
constexpr const GV & getAMDGPUGridValues()
static constexpr GV SPIRVGridValues
For generic SPIR-V GPUs.
OMPDynGroupprivateFallbackType
The fallback types for the dyn_groupprivate clause.
static constexpr GV NVPTXGridValues
For Nvidia GPUs.
@ OMP_TGT_EXEC_MODE_SPMD_NO_LOOP
Function * Kernel
Summary of a kernel (=entry point for target offloading).
Definition OpenMPOpt.h:21
WorksharingLoopType
A type of worksharing loop construct.
OMPAtomicCompareOp
Atomic compare operations. Currently OpenMP only supports ==, >, and <.
NodeAddr< PhiNode * > Phi
Definition RDFGraph.h:390
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h:315
LLVM_ABI BasicBlock * splitBBWithSuffix(IRBuilderBase &Builder, bool CreateBranch, llvm::Twine Suffix=".split")
Like splitBB, but reuses the current block's name for the new name.
@ Offset
Definition DWP.cpp:578
detail::zippy< detail::zip_shortest, T, U, Args... > zip(T &&t, U &&u, Args &&...args)
zip iterator for two or more iteratable types.
Definition STLExtras.h:830
LLVM_ABI unsigned computeUnrollCount(Loop *L, const TargetTransformInfo &TTI, DominatorTree &DT, LoopInfo *LI, AssumptionCache *AC, ScalarEvolution &SE, const SmallPtrSetImpl< const Value * > &EphValues, OptimizationRemarkEmitter *ORE, unsigned TripCount, unsigned MaxTripCount, bool MaxOrZero, unsigned TripMultiple, const UnrollCostEstimator &UCE, TargetTransformInfo::UnrollingPreferences &UP, TargetTransformInfo::PeelingPreferences &PP)
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1739
hash_code hash_value(const FixedPointSemantics &Val)
LLVM_ABI Expected< std::unique_ptr< Module > > parseBitcodeFile(MemoryBufferRef Buffer, LLVMContext &Context, ParserCallbacks Callbacks={})
Read the specified bitcode file, returning the module.
detail::zippy< detail::zip_first, T, U, Args... > zip_equal(T &&t, U &&u, Args &&...args)
zip iterator that assumes that all iteratees have the same length.
Definition STLExtras.h:840
LLVM_ABI BasicBlock * CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap, const Twine &NameSuffix="", Function *F=nullptr, ClonedCodeInfo *CodeInfo=nullptr, bool MapAtoms=true)
Return a copy of the specified basic block, but without embedding the block into a particular functio...
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition STLExtras.h:2554
unsigned getPointerAddressSpace(const Type *T)
Definition SPIRVUtils.h:395
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto successors(const MachineBasicBlock *BB)
@ Load
The value being inserted comes from a load (InsertElement only).
@ Store
The extracted value is stored (ExtractElement only).
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
testing::Matcher< const detail::ErrorHolder & > Failed()
Definition Error.h:198
constexpr from_range_t from_range
auto dyn_cast_if_present(const Y &Val)
dyn_cast_if_present<X> - Functionally identical to dyn_cast, except that a null (or none in the case ...
Definition Casting.h:732
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE()
LLVM_ABI BasicBlock * splitBB(IRBuilderBase::InsertPoint IP, bool CreateBranch, DebugLoc DL, llvm::Twine Name={})
Split a BasicBlock at an InsertPoint, even if the block is degenerate (missing the terminator).
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
Definition STLExtras.h:2208
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:633
LLVM_ABI TargetTransformInfo::UnrollingPreferences gatherUnrollingPreferences(Loop *L, ScalarEvolution &SE, const TargetTransformInfo &TTI, BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, llvm::OptimizationRemarkEmitter &ORE, int OptLevel, std::optional< unsigned > UserThreshold, std::optional< bool > UserAllowPartial, std::optional< bool > UserRuntime, std::optional< bool > UserUpperBound, std::optional< unsigned > UserFullUnrollMaxCount)
Gather the various unrolling parameters based on the defaults, compiler flags, TTI overrides and user...
std::string utostr(uint64_t X, bool isNeg=false)
void * PointerTy
ErrorOr< T > expectedToErrorOrAndEmitErrors(LLVMContext &Ctx, Expected< T > Val)
bool isa_and_nonnull(const Y &Val)
Definition Casting.h:676
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
LLVM_ABI bool convertUsersOfConstantsToInstructions(ArrayRef< Constant * > Consts, Function *RestrictToFunc=nullptr, bool RemoveDeadConstants=true, bool IncludeSelf=false)
Replace constant expressions users of the given constants with instructions.
unsigned Log2_32(uint32_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
Definition MathExtras.h:332
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
LLVM_ABI TargetTransformInfo::PeelingPreferences gatherPeelingPreferences(Loop *L, ScalarEvolution &SE, const TargetTransformInfo &TTI, std::optional< bool > UserAllowPeeling, std::optional< bool > UserAllowProfileBasedPeeling, bool UnrollingSpecficValues=false)
LLVM_ABI void SplitBlockAndInsertIfThenElse(Value *Cond, BasicBlock::iterator SplitBefore, Instruction **ThenTerm, Instruction **ElseTerm, MDNode *BranchWeights=nullptr, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr)
SplitBlockAndInsertIfThenElse is similar to SplitBlockAndInsertIfThen, but also creates the ElseBlock...
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1753
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
CodeGenOptLevel
Code generation optimization level.
Definition CodeGen.h:149
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition Format.h:102
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
AtomicOrdering
Atomic ordering for LLVM's memory model.
constexpr T divideCeil(U Numerator, V Denominator)
Returns the integer ceil(Numerator / Denominator).
Definition MathExtras.h:395
TargetTransformInfo TTI
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
LLVM_ABI bool MergeBlockIntoPredecessor(BasicBlock *BB, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, MemoryDependenceResults *MemDep=nullptr, bool PredecessorWithTwoSuccessors=false, DominatorTree *DT=nullptr)
Attempts to merge a block into its predecessor, if possible.
@ Mul
Product of integers.
@ Add
Sum of integers.
LLVM_ABI BasicBlock * SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt, DominatorTree *DT, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, const Twine &BBName="")
Split the specified block at the specified instruction.
IntPtrTy
Definition InstrProf.h:82
DWARFExpression::Operation Op
LLVM_ABI void remapInstructionsInBlocks(ArrayRef< BasicBlock * > Blocks, ValueToValueMapTy &VMap)
Remaps instructions in Blocks using the mapping in VMap.
ArrayRef(const T &OneElt) -> ArrayRef< T >
OutputIt copy(R &&Range, OutputIt Out)
Definition STLExtras.h:1885
constexpr unsigned BitWidth
ValueMap< const Value *, WeakTrackingVH > ValueToValueMapTy
LLVM_ABI void spliceBB(IRBuilderBase::InsertPoint IP, BasicBlock *New, bool CreateBranch, DebugLoc DL)
Move the instruction after an InsertPoint to the beginning of another BasicBlock.
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
constexpr auto seq(T Begin, T End)
Iterate over an integral type from Begin up to - but not including - End.
Definition Sequence.h:341
auto predecessors(const MachineBasicBlock *BB)
auto filter_to_vector(ContainerTy &&C, PredicateFn &&Pred)
Filter a range to a SmallVector with the element types deduced.
PointerUnion< const Value *, const PseudoSourceValue * > ValueType
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Next
Definition InstrProf.h:147
LLVM_ABI Constant * ConstantFoldInsertValueInstruction(Constant *Agg, Constant *Val, ArrayRef< unsigned > Idxs)
Attempt to constant fold an insertvalue instruction with the specified operands and indices.
@ Continue
Definition DWP.h:26
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
LLVM_ABI void DeleteDeadBlocks(ArrayRef< BasicBlock * > BBs, DomTreeUpdater *DTU=nullptr, bool KeepOneInputPHIs=false)
Delete the specified blocks from BB.
bool to_integer(StringRef S, N &Num, unsigned Base=0)
Convert the string S to an integer of the specified type using the radix Base. If Base is 0,...
static auto filterDbgVars(iterator_range< simple_ilist< DbgRecord >::iterator > R)
Filter the DbgRecord range to DbgVariableRecord types only and downcast.
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
static LLVM_ABI void collectEphemeralValues(const Loop *L, AssumptionCache *AC, SmallPtrSetImpl< const Value * > &EphValues)
Collect a loop's ephemeral values (those used only by an assume or similar intrinsics in the loop).
This struct is a compact representation of a valid (power of two) or undefined (0) alignment.
Definition Alignment.h:106
A struct to pack the relevant information for an OpenMP affinity clause.
a struct to pack relevant information while generating atomic Ops
A struct to pack the relevant information for an OpenMP depend clause.
omp::RTLDependenceKindTy DepKind
A struct to pack static and dynamic dependency information for a task.
LLVM_ABI Error mergeFiniBB(IRBuilderBase &Builder, BasicBlock *ExistingFiniBB)
For cases where there is an unavoidable existing finalization block (e.g.
LLVM_ABI Expected< BasicBlock * > getFiniBB(IRBuilderBase &Builder)
The basic block to which control should be transferred to implement the FiniCB.
Description of a LLVM-IR insertion point (IP) and a debug/source location (filename,...
This structure contains combined information generated for mappable clauses, including base pointers,...
MapDeviceInfoArrayTy DevicePointers
StructNonContiguousInfo NonContigInfo
Helper that contains information about regions we need to outline during finalization.
void collectBlocks(SmallPtrSetImpl< BasicBlock * > &BlockSet, SmallVectorImpl< BasicBlock * > &BlockVector)
Collect all blocks in between EntryBB and ExitBB in both the given vector and set.
virtual std::unique_ptr< CodeExtractor > createCodeExtractor(ArrayRef< BasicBlock * > Blocks, bool ArgsInZeroAddressSpace, Twine Suffix=Twine(""))
Create a CodeExtractor instance based on the information stored in this structure,...
Information about an OpenMP reduction.
EvalKind EvaluationKind
Reduction evaluation kind - scalar, complex or aggregate.
ReductionGenAtomicCBTy AtomicReductionGen
Callback for generating the atomic reduction body, may be null.
ReductionGenCBTy ReductionGen
Callback for generating the reduction body.
Value * Variable
Reduction variable of pointer type.
Value * PrivateVariable
Thread-private partial reduction variable.
ReductionGenClangCBTy ReductionGenClang
Clang callback for generating the reduction body.
Type * ElementType
Reduction element type, must match pointee type of variable.
ReductionGenDataPtrPtrCBTy DataPtrPtrGen
Container for the arguments used to pass data to the runtime library.
Value * SizesArray
The array of sizes passed to the runtime library.
Value * PointersArray
The array of section pointers passed to the runtime library.
Value * MappersArray
The array of user-defined mappers passed to the runtime library.
Value * MapTypesArrayEnd
The array of map types passed to the runtime library for the end of the region, or nullptr if there a...
Value * BasePointersArray
The array of base pointer passed to the runtime library.
Value * MapTypesArray
The array of map types passed to the runtime library for the beginning of the region or for the entir...
Value * MapNamesArray
The array of original declaration names of mapped pointers sent to the runtime library for debugging.
Data structure that contains the needed information to construct the kernel args vector.
bool StrictBlocks
True if the kernel strictly requires the number of blocks and threads above to run.
ArrayRef< Value * > NumThreads
The number of threads.
TargetDataRTArgs RTArgs
Arguments passed to the runtime library.
Value * NumIterations
The number of iterations.
Value * DynCGroupMem
The size of the dynamic shared memory.
unsigned NumTargetItems
Number of arguments passed to the runtime library.
bool HasNoWait
True if the kernel has 'no wait' clause.
ArrayRef< Value * > NumTeams
The number of teams.
omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback
The fallback mechanism for the shared memory.
Container to pass the default attributes with which a kernel must be launched, used to set kernel att...
Container to pass LLVM IR runtime values or constants related to the number of teams and threads with...
Value * DeviceID
Device ID value used in the kernel launch.
Value * LoopTripCount
Total number of iterations of the SPMD or Generic-SPMD kernel or null if it is a generic kernel.
SmallVector< Value * > MaxThreads
'parallel' construct 'num_threads' clause value, if present and it is an SPMD kernel.
Data structure to contain the information needed to uniquely identify a target entry.
static LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, StringRef ParentName, unsigned DeviceID, unsigned FileID, unsigned Line, unsigned Count)
static constexpr const char * KernelNamePrefix
The prefix used for kernel names.
static LLVM_ABI const Target * lookupTarget(const Triple &TheTriple, std::string &Error)
lookupTarget - Lookup a target based on a target triple.
Parameters that control the generic loop unrolling transformation.
unsigned Threshold
The cost threshold for the unrolled loop.
bool Force
Apply loop unroll on any kind of loop (mainly to loops that fail runtime unrolling).
unsigned PartialOptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size, like OptSizeThreshold,...
unsigned PartialThreshold
The cost threshold for the unrolled loop, like Threshold, but used for partial/runtime unrolling (set...
unsigned OptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size (set to UINT_MAX to disable).
Defines various target-specific GPU grid values that must be consistent between host RTL (plugin),...