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 *StrictFlag = Builder.getInt64(KernelArgs.StrictBlocksAndThreads);
667 StrictFlag = Builder.CreateShl(StrictFlag, 6);
668
669 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
670 Flags = Builder.CreateOr(Flags, StrictFlag);
671
672 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
673
674 Value *NumTeams3D =
675 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
676 Value *NumThreads3D =
677 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
678 for (unsigned I :
679 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
680 NumTeams3D =
681 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
682 for (unsigned I :
683 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
684 NumThreads3D =
685 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
686
687 ArgsVector = {Version,
688 PointerNum,
689 KernelArgs.RTArgs.BasePointersArray,
690 KernelArgs.RTArgs.PointersArray,
691 KernelArgs.RTArgs.SizesArray,
692 KernelArgs.RTArgs.MapTypesArray,
693 KernelArgs.RTArgs.MapNamesArray,
694 KernelArgs.RTArgs.MappersArray,
695 KernelArgs.NumIterations,
696 Flags,
697 NumTeams3D,
698 NumThreads3D,
699 KernelArgs.DynCGroupMem};
700}
701
703 LLVMContext &Ctx = Fn.getContext();
704
705 // Get the function's current attributes.
706 auto Attrs = Fn.getAttributes();
707 auto FnAttrs = Attrs.getFnAttrs();
708 auto RetAttrs = Attrs.getRetAttrs();
710 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
711 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
712
713 // Add AS to FnAS while taking special care with integer extensions.
714 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
715 bool Param = true) -> void {
716 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
717 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
718 if (HasSignExt || HasZeroExt) {
719 assert(AS.getNumAttributes() == 1 &&
720 "Currently not handling extension attr combined with others.");
721 if (Param) {
722 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
723 FnAS = FnAS.addAttribute(Ctx, AK);
724 } else if (auto AK =
725 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
726 FnAS = FnAS.addAttribute(Ctx, AK);
727 } else {
728 FnAS = FnAS.addAttributes(Ctx, AS);
729 }
730 };
731
732#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
733#include "llvm/Frontend/OpenMP/OMPKinds.def"
734
735 // Add attributes to the function declaration.
736 switch (FnID) {
737#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
738 case Enum: \
739 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
740 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
741 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
742 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
743 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
744 break;
745#include "llvm/Frontend/OpenMP/OMPKinds.def"
746 default:
747 // Attributes are optional.
748 break;
749 }
750}
751
754 FunctionType *FnTy = nullptr;
755 Function *Fn = nullptr;
756
757 // Try to find the declation in the module first.
758 switch (FnID) {
759#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
760 case Enum: \
761 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
762 IsVarArg); \
763 Fn = M.getFunction(Str); \
764 break;
765#include "llvm/Frontend/OpenMP/OMPKinds.def"
766 }
767
768 if (!Fn) {
769 // Create a new declaration if we need one.
770 switch (FnID) {
771#define OMP_RTL(Enum, Str, ...) \
772 case Enum: \
773 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
774 break;
775#include "llvm/Frontend/OpenMP/OMPKinds.def"
776 }
777 Fn->setCallingConv(Config.getRuntimeCC());
778 // Add information if the runtime function takes a callback function
779 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
780 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
781 LLVMContext &Ctx = Fn->getContext();
782 MDBuilder MDB(Ctx);
783 // Annotate the callback behavior of the runtime function:
784 // - The callback callee is argument number 2 (microtask).
785 // - The first two arguments of the callback callee are unknown (-1).
786 // - All variadic arguments to the runtime function are passed to the
787 // callback callee.
788 Fn->addMetadata(
789 LLVMContext::MD_callback,
791 2, {-1, -1}, /* VarArgsArePassed */ true)}));
792 }
793 }
794
795 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
796 << " with type " << *Fn->getFunctionType() << "\n");
797 addAttributes(FnID, *Fn);
798
799 } else {
800 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
801 << " with type " << *Fn->getFunctionType() << "\n");
802 }
803
804 assert(Fn && "Failed to create OpenMP runtime function");
805
806 return {FnTy, Fn};
807}
808
811 if (!FiniBB) {
812 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
814 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
815 Builder.SetInsertPoint(FiniBB);
816 // FiniCB adds the branch to the exit stub.
817 if (Error Err = FiniCB(Builder.saveIP()))
818 return Err;
819 }
820 return FiniBB;
821}
822
824 BasicBlock *OtherFiniBB) {
825 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
826 if (!FiniBB) {
827 FiniBB = OtherFiniBB;
828
829 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
830 if (Error Err = FiniCB(Builder.saveIP()))
831 return Err;
832
833 return Error::success();
834 }
835
836 // Move instructions from FiniBB to the start of OtherFiniBB.
837 auto EndIt = FiniBB->end();
838 if (FiniBB->size() >= 1)
839 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
840 EndIt = Prev;
841 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
842 EndIt);
843
844 FiniBB->replaceAllUsesWith(OtherFiniBB);
845 FiniBB->eraseFromParent();
846 FiniBB = OtherFiniBB;
847 return Error::success();
848}
849
852 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
853 assert(Fn && "Failed to create OpenMP runtime function pointer");
854 return Fn;
855}
856
859 StringRef Name) {
860 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
861 Call->setCallingConv(Config.getRuntimeCC());
862 return Call;
863}
864
865void OpenMPIRBuilder::initialize() { initializeTypes(M); }
866
869 BasicBlock &EntryBlock = Function->getEntryBlock();
870 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
871
872 // Loop over blocks looking for constant allocas, skipping the entry block
873 // as any allocas there are already in the desired location.
874 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
875 Block++) {
876 for (auto Inst = Block->getReverseIterator()->begin();
877 Inst != Block->getReverseIterator()->end();) {
879 Inst++;
881 continue;
882 AllocaInst->moveBeforePreserving(MoveLocInst);
883 } else {
884 Inst++;
885 }
886 }
887 }
888}
889
892
893 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
894 // TODO: For now, we support simple static allocations, we might need to
895 // move non-static ones as well. However, this will need further analysis to
896 // move the lenght arguments as well.
898 };
899
900 for (llvm::Instruction &Inst : Block)
902 if (ShouldHoistAlloca(*AllocaInst))
903 AllocasToMove.push_back(AllocaInst);
904
905 auto InsertPoint =
906 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
907
908 for (llvm::Instruction *AllocaInst : AllocasToMove)
910}
911
913 PostDominatorTree PostDomTree(*Func);
914 for (llvm::BasicBlock &BB : *Func)
915 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
917}
918
920 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
922 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
923 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
924 // Skip functions that have not finalized yet; may happen with nested
925 // function generation.
926 if (Fn && OI->getFunction() != Fn) {
927 DeferredOutlines.push_back(std::move(OI));
928 continue;
929 }
930
931 ParallelRegionBlockSet.clear();
932 Blocks.clear();
933 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
934
935 Function *OuterFn = OI->getFunction();
936 CodeExtractorAnalysisCache CEAC(*OuterFn);
937 // If we generate code for the target device, we need to allocate
938 // struct for aggregate params in the device default alloca address space.
939 // OpenMP runtime requires that the params of the extracted functions are
940 // passed as zero address space pointers. This flag ensures that
941 // CodeExtractor generates correct code for extracted functions
942 // which are used by OpenMP runtime.
943 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
944 std::unique_ptr<CodeExtractor> Extractor =
945 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
946
947 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
948 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
949 << " Exit: " << OI->ExitBB->getName() << "\n");
950 assert(Extractor->isEligible() &&
951 "Expected OpenMP outlining to be possible!");
952
953 for (auto *V : OI->ExcludeArgsFromAggregate)
954 Extractor->excludeArgFromAggregate(V);
955
956 Function *OutlinedFn =
957 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
958
959 // Forward target-cpu, target-features attributes to the outlined function.
960 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
961 if (TargetCpuAttr.isStringAttribute())
962 OutlinedFn->addFnAttr(TargetCpuAttr);
963
964 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
965 if (TargetFeaturesAttr.isStringAttribute())
966 OutlinedFn->addFnAttr(TargetFeaturesAttr);
967
968 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
969 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
970 assert(OutlinedFn->getReturnType()->isVoidTy() &&
971 "OpenMP outlined functions should not return a value!");
972
973 // For compability with the clang CG we move the outlined function after the
974 // one with the parallel region.
975 OutlinedFn->removeFromParent();
976 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
977
978 // Remove the artificial entry introduced by the extractor right away, we
979 // made our own entry block after all.
980 {
981 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
982 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
983 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
984 // Move instructions from the to-be-deleted ArtificialEntry to the entry
985 // basic block of the parallel region. CodeExtractor generates
986 // instructions to unwrap the aggregate argument and may sink
987 // allocas/bitcasts for values that are solely used in the outlined region
988 // and do not escape.
989 assert(!ArtificialEntry.empty() &&
990 "Expected instructions to add in the outlined region entry");
991 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
992 End = ArtificialEntry.rend();
993 It != End;) {
994 Instruction &I = *It;
995 It++;
996
997 if (I.isTerminator()) {
998 // Absorb any debug value that terminator may have
999 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
1000 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
1001 continue;
1002 }
1003
1004 I.moveBeforePreserving(*OI->EntryBB,
1005 OI->EntryBB->getFirstInsertionPt());
1006 }
1007
1008 OI->EntryBB->moveBefore(&ArtificialEntry);
1009 ArtificialEntry.eraseFromParent();
1010 }
1011 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
1012 assert(OutlinedFn && OutlinedFn->hasNUses(1));
1013
1014 // Run a user callback, e.g. to add attributes.
1015 if (OI->PostOutlineCB)
1016 OI->PostOutlineCB(*OutlinedFn);
1017
1018 if (OI->FixUpNonEntryAllocas)
1020 }
1021
1022 // Remove work items that have been completed.
1023 OutlineInfos = std::move(DeferredOutlines);
1024
1025 // The createTarget functions embeds user written code into
1026 // the target region which may inject allocas which need to
1027 // be moved to the entry block of our target or risk malformed
1028 // optimisations by later passes, this is only relevant for
1029 // the device pass which appears to be a little more delicate
1030 // when it comes to optimisations (however, we do not block on
1031 // that here, it's up to the inserter to the list to do so).
1032 // This notbaly has to occur after the OutlinedInfo candidates
1033 // have been extracted so we have an end product that will not
1034 // be implicitly adversely affected by any raises unless
1035 // intentionally appended to the list.
1036 // NOTE: This only does so for ConstantData, it could be extended
1037 // to ConstantExpr's with further effort, however, they should
1038 // largely be folded when they get here. Extending it to runtime
1039 // defined/read+writeable allocation sizes would be non-trivial
1040 // (need to factor in movement of any stores to variables the
1041 // allocation size depends on, as well as the usual loads,
1042 // otherwise it'll yield the wrong result after movement) and
1043 // likely be more suitable as an LLVM optimisation pass.
1046
1047 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1048 [](EmitMetadataErrorKind Kind,
1049 const TargetRegionEntryInfo &EntryInfo) -> void {
1050 errs() << "Error of kind: " << Kind
1051 << " when emitting offload entries and metadata during "
1052 "OMPIRBuilder finalization \n";
1053 };
1054
1055 if (!OffloadInfoManager.empty())
1057
1058 // Rewrite uses of globals to their replacement declare target globals if
1059 // we are processing a device module.
1060 if (Config.isTargetDevice())
1061 applyDeclareTargetGlobalReplacements();
1062
1063 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1064 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1065 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1066 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1067 }
1068
1069 IsFinalized = true;
1070}
1071
1072bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1073
1075 GlobalValue *Original, GlobalValue *Replacement) {
1076 assert(Original && Replacement &&
1077 "Null values provided to registerDeclareTargetGlobalReplacement");
1078 DeclareTargetGlobalReplacements.push_back({Original, Replacement});
1079}
1080
1081void OpenMPIRBuilder::applyDeclareTargetGlobalReplacements() {
1082 for (DeclareTargetGlobalReplacement &R : DeclareTargetGlobalReplacements) {
1083 GlobalValue *OldGV = R.Original;
1084 GlobalValue *NewGV = R.Replacement;
1085
1086 assert(OldGV && NewGV &&
1087 "A null value was inserted into DeclareTargetGlobalReplacements");
1088
1089 // The assert above should catch this case, but this is kept to attempt
1090 // to proceed without issue when asserts are off.
1091 if (!OldGV || !NewGV)
1092 continue;
1093
1094 // The replacement global is a reference pointer that holds the
1095 // address of the device-resident storage. Every use must load the
1096 // reference pointer first and use the loaded address.
1097 //
1098 // Constant expression users (e.g. a constant GEP embedded in another
1099 // global's initializer or in an instruction) cannot have a load inserted
1100 // in place, so first expand any constant-expression users that live inside
1101 // functions into instructions. Any remaining constant users are handled
1102 // via a direct constant rewrite below as we cannot materialize a load
1103 // there.
1104 //
1105 // NOTE: We extend the constant rewrite to module scope, as we replace all
1106 // usages.
1107 if (auto *OldConst = dyn_cast<Constant>(OldGV))
1109 /*RestrictToFunc=*/nullptr,
1110 /*RemoveDeadConstants=*/false);
1111
1112 IRBuilderBase::InsertPointGuard Guard(Builder);
1114 for (User *U : Users) {
1115 auto *Insn = dyn_cast<Instruction>(U);
1116 if (!Insn)
1117 continue;
1118
1119 // A PHI node cannot have a load inserted immediately before it, as PHIs
1120 // must remain grouped at the top of their basic block. So we need to
1121 // make sure any loads we emit are generated in the preceding edge, a
1122 // PHI may reference the global on more than one edge, so every matching
1123 // slot must be handled.
1124 if (auto *PHI = dyn_cast<PHINode>(Insn)) {
1125 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I < E; ++I) {
1126 if (PHI->getIncomingValue(I) != OldGV)
1127 continue;
1128
1129 BasicBlock *IncomingBB = PHI->getIncomingBlock(I);
1130 Builder.SetInsertPoint(IncomingBB->getTerminator());
1131 Builder.SetCurrentDebugLocation(PHI->getDebugLoc());
1132 LoadInst *EdgeLoad = Builder.CreateLoad(NewGV->getType(), NewGV);
1133 PHI->setIncomingValue(I, EdgeLoad);
1134 }
1135 continue;
1136 }
1137
1138 Builder.SetInsertPoint(Insn);
1139 Builder.SetCurrentDebugLocation(Insn->getDebugLoc());
1140 LoadInst *Load = Builder.CreateLoad(NewGV->getType(), NewGV);
1141
1142 // The replacement declare target global lives in the default address
1143 // space, whereas the original global may reside in a non-default
1144 // address space. In that case the initial lowering may have
1145 // emitted an addrspacecast that is no longer valid. Replace the
1146 // whole addrspacecast with the load and erase it rather than
1147 // feeding the load back into the (now pointless) cast.
1148 // NOTE: If we end up with replacement declare target globals in
1149 // non-zero AS's the below will need some minor extensions to have the
1150 // option to alter the address space cast to the new address space where
1151 // required rather than just replacing it.
1152 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Insn)) {
1153 unsigned NewGVAS = NewGV->getType()->getPointerAddressSpace();
1154 assert(NewGVAS == 0 &&
1155 "Non-default address space declare target global");
1156 unsigned OldGVAS = OldGV->getType()->getPointerAddressSpace();
1157 unsigned DestAS = ASC->getType()->getPointerAddressSpace();
1158 if (DestAS == 0 && NewGVAS != OldGVAS) {
1159 ASC->replaceAllUsesWith(Load);
1160 ASC->eraseFromParent();
1161 continue;
1162 }
1163 }
1164
1165 Insn->replaceUsesOfWith(OldGV, Load);
1166 }
1167 }
1168
1170}
1171
1173 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1174}
1175
1177 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1178 auto *GV =
1179 new GlobalVariable(M, I32Ty,
1180 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1181 ConstantInt::get(I32Ty, Value), Name);
1182 GV->setVisibility(GlobalValue::HiddenVisibility);
1183
1184 return GV;
1185}
1186
1188 if (List.empty())
1189 return;
1190
1191 // Convert List to what ConstantArray needs.
1193 UsedArray.resize(List.size());
1194 for (unsigned I = 0, E = List.size(); I != E; ++I)
1196 cast<Constant>(&*List[I]), Builder.getPtrTy());
1197
1198 if (UsedArray.empty())
1199 return;
1200 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1201
1202 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1203 ConstantArray::get(ATy, UsedArray), Name);
1204
1205 GV->setSection("llvm.metadata");
1206}
1207
1210 OMPTgtExecModeFlags Mode) {
1211 auto *Int8Ty = Builder.getInt8Ty();
1212 auto *GVMode = new GlobalVariable(
1213 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1214 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1215 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1216 return GVMode;
1217}
1218
1220 uint32_t SrcLocStrSize,
1221 IdentFlag LocFlags,
1222 unsigned Reserve2Flags) {
1223 // Enable "C-mode".
1224 LocFlags |= OMP_IDENT_FLAG_KMPC;
1225
1226 Constant *&Ident =
1227 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1228 if (!Ident) {
1229 Constant *I32Null = ConstantInt::getNullValue(Int32);
1230 Constant *IdentData[] = {I32Null,
1231 ConstantInt::get(Int32, uint32_t(LocFlags)),
1232 ConstantInt::get(Int32, Reserve2Flags),
1233 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1234
1235 size_t SrcLocStrArgIdx = 4;
1236 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1238 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1239 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1240 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1241 Constant *Initializer =
1242 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1243
1244 // Look for existing encoding of the location + flags, not needed but
1245 // minimizes the difference to the existing solution while we transition.
1246 for (GlobalVariable &GV : M.globals())
1247 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1248 if (GV.getInitializer() == Initializer)
1249 Ident = &GV;
1250
1251 if (!Ident) {
1252 auto *GV = new GlobalVariable(
1253 M, OpenMPIRBuilder::Ident,
1254 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1256 M.getDataLayout().getDefaultGlobalsAddressSpace());
1257 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1258 GV->setAlignment(Align(8));
1259 Ident = GV;
1260 }
1261 }
1262
1263 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1264}
1265
1267 uint32_t &SrcLocStrSize) {
1268 SrcLocStrSize = LocStr.size();
1269 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1270 if (!SrcLocStr) {
1271 Constant *Initializer =
1272 ConstantDataArray::getString(M.getContext(), LocStr);
1273
1274 // Look for existing encoding of the location, not needed but minimizes the
1275 // difference to the existing solution while we transition.
1276 for (GlobalVariable &GV : M.globals())
1277 if (GV.isConstant() && GV.hasInitializer() &&
1278 GV.getInitializer() == Initializer)
1279 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1280
1281 SrcLocStr = Builder.CreateGlobalString(
1282 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1283 &M);
1284 }
1285 return SrcLocStr;
1286}
1287
1289 StringRef FileName,
1290 unsigned Line, unsigned Column,
1291 uint32_t &SrcLocStrSize) {
1292 SmallString<128> Buffer;
1293 Buffer.push_back(';');
1294 Buffer.append(FileName);
1295 Buffer.push_back(';');
1296 Buffer.append(FunctionName);
1297 Buffer.push_back(';');
1298 Buffer.append(std::to_string(Line));
1299 Buffer.push_back(';');
1300 Buffer.append(std::to_string(Column));
1301 Buffer.push_back(';');
1302 Buffer.push_back(';');
1303 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1304}
1305
1306Constant *
1308 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1309 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1310}
1311
1313 uint32_t &SrcLocStrSize,
1314 Function *F) {
1315 DILocation *DIL = DL.get();
1316 if (!DIL)
1317 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1318 StringRef FileName =
1319 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1320 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1321 if (Function.empty() && F)
1322 Function = F->getName();
1323 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1324 DIL->getColumn(), SrcLocStrSize);
1325}
1326
1328 uint32_t &SrcLocStrSize) {
1329 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1330 Loc.IP.getBlock()->getParent());
1331}
1332
1335 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1336 "omp_global_thread_num");
1337}
1338
1339OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1340 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1341 ArrayRef<Type *> ResultPtrTys,
1342 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1343 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1344 "expected one result pointer type per in_reduction item");
1345 if (!updateToLocation(Loc))
1346 return Loc.IP;
1347 if (OrigPtrs.empty())
1348 return Builder.saveIP();
1349
1350 // Compute the executing thread's gtid once for the whole target body and
1351 // reuse it for every in_reduction lookup, so a target with several
1352 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1353 // item.
1354 uint32_t SrcLocStrSize;
1355 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1356 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1357 Value *Gtid = getOrCreateThreadID(Ident);
1358
1359 // The runtime entry point takes (and returns) a generic, default-address-
1360 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1361 // taskgroups to find the matching task_reduction registration for the item.
1362 Type *PtrTy = PointerType::getUnqual(M.getContext());
1363 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1364 FunctionCallee GetThData =
1365 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1366
1367 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1368 // Normalize a non-default-address-space original pointer to the generic
1369 // address space before the call.
1370 Value *OrigPtr = OrigPtrs[Idx];
1371 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1372 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1373 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1374
1375 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1376 "omp.inred.priv");
1377
1378 // Cast the returned private pointer back to the requested address space
1379 // when it differs.
1380 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1381 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1382 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1383
1384 MapPrivateCB(Idx, Priv);
1385 }
1386 return Builder.saveIP();
1387}
1388
1391 bool ForceSimpleCall, bool CheckCancelFlag) {
1392 if (!updateToLocation(Loc))
1393 return Loc.IP;
1394
1395 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1396 // __kmpc_barrier(loc, thread_id);
1397
1398 IdentFlag BarrierLocFlags;
1399 switch (Kind) {
1400 case OMPD_for:
1401 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1402 break;
1403 case OMPD_sections:
1404 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1405 break;
1406 case OMPD_single:
1407 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1408 break;
1409 case OMPD_barrier:
1410 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1411 break;
1412 default:
1413 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1414 break;
1415 }
1416
1417 uint32_t SrcLocStrSize;
1418 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1419 Value *Args[] = {
1420 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1421 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1422
1423 // If we are in a cancellable parallel region, barriers are cancellation
1424 // points.
1425 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1426 bool UseCancelBarrier =
1427 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1428
1430 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1431 ? OMPRTL___kmpc_cancel_barrier
1432 : OMPRTL___kmpc_barrier),
1433 Args);
1434
1435 if (UseCancelBarrier && CheckCancelFlag)
1436 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1437 return Err;
1438
1439 return Builder.saveIP();
1440}
1441
1444 Value *IfCondition,
1445 omp::Directive CanceledDirective) {
1446 if (!updateToLocation(Loc))
1447 return Loc.IP;
1448
1449 // LLVM utilities like blocks with terminators.
1450 auto *UI = Builder.CreateUnreachable();
1451
1452 Instruction *ThenTI = UI, *ElseTI = nullptr;
1453 if (IfCondition) {
1454 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1455
1456 // Even if the if condition evaluates to false, this should count as a
1457 // cancellation point
1458 Builder.SetInsertPoint(ElseTI);
1459 auto ElseIP = Builder.saveIP();
1460
1462 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1463 if (!IPOrErr)
1464 return IPOrErr;
1465 }
1466
1467 Builder.SetInsertPoint(ThenTI);
1468
1469 Value *CancelKind = nullptr;
1470 switch (CanceledDirective) {
1471#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1472 case DirectiveEnum: \
1473 CancelKind = Builder.getInt32(Value); \
1474 break;
1475#include "llvm/Frontend/OpenMP/OMPKinds.def"
1476 default:
1477 llvm_unreachable("Unknown cancel kind!");
1478 }
1479
1480 uint32_t SrcLocStrSize;
1481 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1482 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1483 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1485 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1486
1487 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1488 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1489 return Err;
1490
1491 // Update the insertion point and remove the terminator we introduced.
1492 Builder.SetInsertPoint(UI->getParent());
1493 UI->eraseFromParent();
1494
1495 return Builder.saveIP();
1496}
1497
1500 omp::Directive CanceledDirective) {
1501 if (!updateToLocation(Loc))
1502 return Loc.IP;
1503
1504 // LLVM utilities like blocks with terminators.
1505 auto *UI = Builder.CreateUnreachable();
1506 Builder.SetInsertPoint(UI);
1507
1508 Value *CancelKind = nullptr;
1509 switch (CanceledDirective) {
1510#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1511 case DirectiveEnum: \
1512 CancelKind = Builder.getInt32(Value); \
1513 break;
1514#include "llvm/Frontend/OpenMP/OMPKinds.def"
1515 default:
1516 llvm_unreachable("Unknown cancel kind!");
1517 }
1518
1519 uint32_t SrcLocStrSize;
1520 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1521 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1522 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1524 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1525
1526 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1527 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1528 return Err;
1529
1530 // Update the insertion point and remove the terminator we introduced.
1531 Builder.SetInsertPoint(UI->getParent());
1532 UI->eraseFromParent();
1533
1534 return Builder.saveIP();
1535}
1536
1538 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1539 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1540 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1541 if (!updateToLocation(Loc))
1542 return Loc.IP;
1543
1544 Builder.restoreIP(AllocaIP);
1545 auto *KernelArgsPtr =
1546 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1548
1549 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1550 llvm::Value *Arg =
1551 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1552 Builder.CreateAlignedStore(
1553 KernelArgs[I], Arg,
1554 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1555 }
1556
1557 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1558 NumThreads, HostPtr, KernelArgsPtr};
1559
1561 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1562 OffloadingArgs);
1563
1564 return Builder.saveIP();
1565}
1566
1568 const LocationDescription &Loc, Value *OutlinedFnID,
1569 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1570 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1571
1572 if (!updateToLocation(Loc))
1573 return Loc.IP;
1574
1575 // On top of the arrays that were filled up, the target offloading call
1576 // takes as arguments the device id as well as the host pointer. The host
1577 // pointer is used by the runtime library to identify the current target
1578 // region, so it only has to be unique and not necessarily point to
1579 // anything. It could be the pointer to the outlined function that
1580 // implements the target region, but we aren't using that so that the
1581 // compiler doesn't need to keep that, and could therefore inline the host
1582 // function if proven worthwhile during optimization.
1583
1584 // From this point on, we need to have an ID of the target region defined.
1585 assert(OutlinedFnID && "Invalid outlined function ID!");
1586 (void)OutlinedFnID;
1587
1588 // Return value of the runtime offloading call.
1589 Value *Return = nullptr;
1590
1591 // Arguments for the target kernel.
1592 SmallVector<Value *> ArgsVector;
1593 getKernelArgsVector(Args, Builder, ArgsVector);
1594
1595 // The target region is an outlined function launched by the runtime
1596 // via calls to __tgt_target_kernel().
1597 //
1598 // Note that on the host and CPU targets, the runtime implementation of
1599 // these calls simply call the outlined function without forking threads.
1600 // The outlined functions themselves have runtime calls to
1601 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1602 // the compiler in emitTeamsCall() and emitParallelCall().
1603 //
1604 // In contrast, on the NVPTX target, the implementation of
1605 // __tgt_target_teams() launches a GPU kernel with the requested number
1606 // of teams and threads so no additional calls to the runtime are required.
1607 // Check the error code and execute the host version if required.
1608 Builder.restoreIP(emitTargetKernel(
1609 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1610 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1611
1612 BasicBlock *OffloadFailedBlock =
1613 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1614 BasicBlock *OffloadContBlock =
1615 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1616 Value *Failed = Builder.CreateIsNotNull(Return);
1617 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1618
1619 auto CurFn = Builder.GetInsertBlock()->getParent();
1620 emitBlock(OffloadFailedBlock, CurFn);
1621 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1622 if (!AfterIP)
1623 return AfterIP.takeError();
1624 Builder.restoreIP(*AfterIP);
1625 emitBranch(OffloadContBlock);
1626 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1627 return Builder.saveIP();
1628}
1629
1631 Value *CancelFlag, omp::Directive CanceledDirective) {
1632 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1633 "Unexpected cancellation!");
1634
1635 // For a cancel barrier we create two new blocks.
1636 BasicBlock *BB = Builder.GetInsertBlock();
1637 BasicBlock *NonCancellationBlock;
1638 if (Builder.GetInsertPoint() == BB->end()) {
1639 // TODO: This branch will not be needed once we moved to the
1640 // OpenMPIRBuilder codegen completely.
1641 NonCancellationBlock = BasicBlock::Create(
1642 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1643 } else {
1644 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1646 Builder.SetInsertPoint(BB);
1647 }
1648 BasicBlock *CancellationBlock = BasicBlock::Create(
1649 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1650
1651 // Jump to them based on the return value.
1652 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1653 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1654 /* TODO weight */ nullptr, nullptr);
1655
1656 // From the cancellation block we finalize all variables and go to the
1657 // post finalization block that is known to the FiniCB callback.
1658 auto &FI = FinalizationStack.back();
1659 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1660 if (!FiniBBOrErr)
1661 return FiniBBOrErr.takeError();
1662 Builder.SetInsertPoint(CancellationBlock);
1663 Builder.CreateBr(*FiniBBOrErr);
1664
1665 // The continuation block is where code generation continues.
1666 Builder.SetInsertPoint(NonCancellationBlock, NonCancellationBlock->begin());
1667 return Error::success();
1668}
1669
1670/// Create wrapper function used to gather the outlined function's argument
1671/// structure from a shared buffer and to forward them to it when running in
1672/// Generic mode.
1673///
1674/// The outlined function is expected to receive 2 integer arguments followed by
1675/// an optional pointer argument to an argument structure holding the rest.
1677 Function &OutlinedFn) {
1678 size_t NumArgs = OutlinedFn.arg_size();
1679 assert((NumArgs == 2 || NumArgs == 3) &&
1680 "expected a 2-3 argument parallel outlined function");
1681 bool UseArgStruct = NumArgs == 3;
1682
1683 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1684 IRBuilder<>::InsertPointGuard IPG(Builder);
1685 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1686 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1687 /*isVarArg=*/false);
1688 auto *WrapperFn =
1690 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1691
1692 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1693 WrapperFn->addParamAttr(0, Attribute::ZExt);
1694 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1695
1696 BasicBlock *EntryBB =
1697 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1698 Builder.SetInsertPoint(EntryBB);
1699
1700 // Allocation.
1701 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1702 /*ArraySize=*/nullptr, "addr");
1703 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1704 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1705 AddrAlloca->getName() + ".ascast");
1706
1707 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1708 /*ArraySize=*/nullptr, "zero");
1709 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1710 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1711 ZeroAlloca->getName() + ".ascast");
1712
1713 Value *ArgsAlloca = nullptr;
1714 if (UseArgStruct) {
1715 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1716 /*ArraySize=*/nullptr, "global_args");
1717 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1718 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1719 ArgsAlloca->getName() + ".ascast");
1720 }
1721
1722 // Initialization.
1723 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1724 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1725 if (UseArgStruct) {
1726 Builder.CreateCall(
1727 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1728 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1729 {ArgsAlloca});
1730 }
1731
1732 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1733
1734 // Load structArg from global_args.
1735 if (UseArgStruct) {
1736 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1737 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1738 {Builder.getInt64(0)});
1739 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1740 Args.push_back(StructArg);
1741 }
1742
1743 // Call the outlined function holding the parallel body.
1744 Builder.CreateCall(&OutlinedFn, Args);
1745 Builder.CreateRetVoid();
1746
1747 return WrapperFn;
1748}
1749
1750// Callback used to create OpenMP runtime calls to support
1751// omp parallel clause for the device.
1752// We need to use this callback to replace call to the OutlinedFn in OuterFn
1753// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1755 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1756 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1757 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1758 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1759 assert(OutlinedFn.arg_size() >= 2 &&
1760 "Expected at least tid and bounded tid as arguments");
1761 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1762
1763 // Add some known attributes.
1764 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1765 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1766 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1767 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1768 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1769 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1770
1771 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1772 assert(CI && "Expected call instruction to outlined function");
1773 CI->getParent()->setName("omp_parallel");
1774
1775 Builder.SetInsertPoint(CI);
1776 Type *PtrTy = OMPIRBuilder->VoidPtr;
1777
1778 // Add alloca for kernel args
1779 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1780 Builder.SetInsertPoint(OuterAllocaBB, OuterAllocaBB->getFirstInsertionPt());
1781 AllocaInst *ArgsAlloca =
1782 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1783 Value *Args = ArgsAlloca;
1784 // Add address space cast if array for storing arguments is not allocated
1785 // in address space 0
1786 if (ArgsAlloca->getAddressSpace())
1787 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1788 Builder.restoreIP(CurrentIP);
1789
1790 // Store captured vars which are used by kmpc_parallel_60
1791 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1792 Value *V = *(CI->arg_begin() + 2 + Idx);
1793 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1794 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1795 Builder.CreateStore(V, StoreAddress);
1796 }
1797
1798 Value *Cond =
1799 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1800 : Builder.getInt32(1);
1801 Value *NumThreadsArg =
1802 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1803 : Builder.getInt32(-1);
1804
1805 // If this is not a Generic kernel, we can skip generating the wrapper.
1806 Value *WrapperFn;
1807 if (isGenericKernel(*OuterFn))
1808 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1809 else
1810 WrapperFn = Constant::getNullValue(PtrTy);
1811
1812 // Build kmpc_parallel_60 call
1813 Value *Parallel60CallArgs[] = {
1814 /* identifier*/ Ident,
1815 /* global thread num*/ ThreadID,
1816 /* if expression */ Cond,
1817 /* number of threads */ NumThreadsArg,
1818 /* Proc bind */ Builder.getInt32(-1),
1819 /* outlined function */ &OutlinedFn,
1820 /* wrapper function */ WrapperFn,
1821 /* arguments of the outlined funciton*/ Args,
1822 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1823 /* strict for number of threads */ Builder.getInt32(0)};
1824
1825 FunctionCallee RTLFn =
1826 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1827
1828 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1829
1830 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1831 << *Builder.GetInsertBlock()->getParent() << "\n");
1832
1833 // Initialize the local TID stack location with the argument value.
1834 Builder.SetInsertPoint(PrivTID);
1835 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1836 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1837 PrivTIDAddr);
1838
1839 // Remove redundant call to the outlined function.
1840 CI->eraseFromParent();
1841
1842 for (Instruction *I : ToBeDeleted) {
1843 I->eraseFromParent();
1844 }
1845}
1846
1847// Callback used to create OpenMP runtime calls to support
1848// omp parallel clause for the host.
1849// We need to use this callback to replace call to the OutlinedFn in OuterFn
1850// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1851static void
1853 Function *OuterFn, Value *Ident, Value *IfCondition,
1854 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1855 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1856 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1857 FunctionCallee RTLFn;
1858 if (IfCondition) {
1859 RTLFn =
1860 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1861 } else {
1862 RTLFn =
1863 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1864 }
1865 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1866 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1867 LLVMContext &Ctx = F->getContext();
1868 MDBuilder MDB(Ctx);
1869 // Annotate the callback behavior of the __kmpc_fork_call:
1870 // - The callback callee is argument number 2 (microtask).
1871 // - The first two arguments of the callback callee are unknown (-1).
1872 // - All variadic arguments to the __kmpc_fork_call are passed to the
1873 // callback callee.
1874 F->addMetadata(LLVMContext::MD_callback,
1876 2, {-1, -1},
1877 /* VarArgsArePassed */ true)}));
1878 }
1879 }
1880 // Add some known attributes.
1881 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1882 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1883 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1884
1885 assert(OutlinedFn.arg_size() >= 2 &&
1886 "Expected at least tid and bounded tid as arguments");
1887 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1888
1889 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1890 CI->getParent()->setName("omp_parallel");
1891 Builder.SetInsertPoint(CI);
1892
1893 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1894 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1895 &OutlinedFn};
1896
1897 SmallVector<Value *, 16> RealArgs;
1898 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1899 if (IfCondition) {
1900 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1901 RealArgs.push_back(Cond);
1902 }
1903 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1904
1905 // __kmpc_fork_call_if always expects a void ptr as the last argument
1906 // If there are no arguments, pass a null pointer.
1907 auto PtrTy = OMPIRBuilder->VoidPtr;
1908 if (IfCondition && NumCapturedVars == 0) {
1909 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1910 RealArgs.push_back(NullPtrValue);
1911 }
1912
1913 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1914
1915 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1916 << *Builder.GetInsertBlock()->getParent() << "\n");
1917
1918 // Initialize the local TID stack location with the argument value.
1919 Builder.SetInsertPoint(PrivTID);
1920 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1921 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1922 PrivTIDAddr);
1923
1924 // Remove redundant call to the outlined function.
1925 CI->eraseFromParent();
1926
1927 for (Instruction *I : ToBeDeleted) {
1928 I->eraseFromParent();
1929 }
1930}
1931
1933 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1934 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1935 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1936 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1937 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1938
1939 if (!updateToLocation(Loc))
1940 return Loc.IP;
1941
1942 uint32_t SrcLocStrSize;
1943 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1944 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1945 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1946 (ProcBind != OMP_PROC_BIND_default);
1947 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1948 // If we generate code for the target device, we need to allocate
1949 // struct for aggregate params in the device default alloca address space.
1950 // OpenMP runtime requires that the params of the extracted functions are
1951 // passed as zero address space pointers. This flag ensures that extracted
1952 // function arguments are declared in zero address space
1953 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1954
1955 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1956 // only if we compile for host side.
1957 if (NumThreads && !Config.isTargetDevice()) {
1958 Value *Args[] = {
1959 Ident, ThreadID,
1960 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
1962 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
1963 }
1964
1965 if (ProcBind != OMP_PROC_BIND_default) {
1966 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
1967 Value *Args[] = {
1968 Ident, ThreadID,
1969 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
1971 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
1972 }
1973
1974 BasicBlock *InsertBB = Builder.GetInsertBlock();
1975 Function *OuterFn = InsertBB->getParent();
1976
1977 // Save the outer alloca block because the insertion iterator may get
1978 // invalidated and we still need this later.
1979 BasicBlock *OuterAllocaBlock = OuterAllocIP.getBlock();
1980
1981 // Vector to remember instructions we used only during the modeling but which
1982 // we want to delete at the end.
1984
1985 // Change the location to the outer alloca insertion point to create and
1986 // initialize the allocas we pass into the parallel region.
1987 InsertPointTy NewOuter(OuterAllocaBlock, OuterAllocaBlock->begin());
1988 Builder.restoreIP(NewOuter);
1989 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
1990 AllocaInst *ZeroAddrAlloca =
1991 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
1992 Instruction *TIDAddr = TIDAddrAlloca;
1993 Instruction *ZeroAddr = ZeroAddrAlloca;
1994 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
1995 // Add additional casts to enforce pointers in zero address space
1996 TIDAddr = new AddrSpaceCastInst(
1997 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
1998 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
1999 ToBeDeleted.push_back(TIDAddr);
2000 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2001 PointerType ::get(M.getContext(), 0),
2002 "zero.addr.ascast");
2003 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2004 ToBeDeleted.push_back(ZeroAddr);
2005 }
2006
2007 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2008 // associated arguments in the outlined function, so we delete them later.
2009 ToBeDeleted.push_back(TIDAddrAlloca);
2010 ToBeDeleted.push_back(ZeroAddrAlloca);
2011
2012 // Create an artificial insertion point that will also ensure the blocks we
2013 // are about to split are not degenerated.
2014 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2015
2016 BasicBlock *EntryBB = UI->getParent();
2017 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2018 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2019 BasicBlock *PRegPreFiniBB =
2020 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2021 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2022
2023 auto FiniCBWrapper = [&](InsertPointTy IP) {
2024 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2025 // target to the region exit block.
2026 if (IP.getBlock()->end() == IP.getPoint()) {
2028 Builder.restoreIP(IP);
2029 Instruction *I = Builder.CreateBr(PRegExitBB);
2030 IP = InsertPointTy(I->getParent(), I->getIterator());
2031 }
2032 assert(IP.getBlock()->getTerminator()->getNumSuccessors() == 1 &&
2033 IP.getBlock()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2034 "Unexpected insertion point for finalization call!");
2035 return FiniCB(IP);
2036 };
2037
2038 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2039
2040 // Generate the privatization allocas in the block that will become the entry
2041 // of the outlined function.
2042 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2043 InsertPointTy InnerAllocaIP = Builder.saveIP();
2044
2045 AllocaInst *PrivTIDAddr =
2046 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2047 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2048
2049 // Add some fake uses for OpenMP provided arguments.
2050 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2051 Instruction *ZeroAddrUse =
2052 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2053 ToBeDeleted.push_back(ZeroAddrUse);
2054
2055 // EntryBB
2056 // |
2057 // V
2058 // PRegionEntryBB <- Privatization allocas are placed here.
2059 // |
2060 // V
2061 // PRegionBodyBB <- BodeGen is invoked here.
2062 // |
2063 // V
2064 // PRegPreFiniBB <- The block we will start finalization from.
2065 // |
2066 // V
2067 // PRegionExitBB <- A common exit to simplify block collection.
2068 //
2069
2070 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2071
2072 // Let the caller create the body.
2073 assert(BodyGenCB && "Expected body generation callback!");
2074 InsertPointTy CodeGenIP(PRegBodyBB, PRegBodyBB->begin());
2075 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2076 return Err;
2077
2078 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2079
2080 // If OuterFn is a Generic kernel, we need to use device shared memory to
2081 // allocate argument structures. Otherwise, we use stack allocations as usual.
2082 bool UsesDeviceSharedMemory =
2083 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2084 std::unique_ptr<OutlineInfo> OI =
2085 UsesDeviceSharedMemory
2086 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2087 : std::make_unique<OutlineInfo>();
2088
2089 if (Config.isTargetDevice()) {
2090 // Generate OpenMP target specific runtime call
2091 OI->PostOutlineCB = [=, ToBeDeletedVec =
2092 std::move(ToBeDeleted)](Function &OutlinedFn) {
2093 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2094 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2095 ThreadID, ToBeDeletedVec);
2096 };
2097 } else {
2098 // Generate OpenMP host runtime call
2099 OI->PostOutlineCB = [=, ToBeDeletedVec =
2100 std::move(ToBeDeleted)](Function &OutlinedFn) {
2101 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2102 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2103 };
2104 }
2105
2106 OI->FixUpNonEntryAllocas = true;
2107 OI->OuterAllocBB = OuterAllocaBlock;
2108 OI->EntryBB = PRegEntryBB;
2109 OI->ExitBB = PRegExitBB;
2110 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2111 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2112
2113 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2115 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2116
2117 CodeExtractorAnalysisCache CEAC(*OuterFn);
2118 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2119 /* AggregateArgs */ false,
2120 /* BlockFrequencyInfo */ nullptr,
2121 /* BranchProbabilityInfo */ nullptr,
2122 /* AssumptionCache */ nullptr,
2123 /* AllowVarArgs */ true,
2124 /* AllowAlloca */ true,
2125 /* AllocationBlock */ OuterAllocaBlock,
2126 /* DeallocationBlocks */ {},
2127 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2128
2129 // Find inputs to, outputs from the code region.
2130 BasicBlock *CommonExit = nullptr;
2131 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2132 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2133
2134 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2135 /*CollectGlobalInputs=*/true);
2136
2137 Inputs.remove_if([&](Value *I) {
2139 return GV->getValueType() == OpenMPIRBuilder::Ident;
2140
2141 return false;
2142 });
2143
2144 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2145
2146 FunctionCallee TIDRTLFn =
2147 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2148
2149 auto PrivHelper = [&](Value &V) -> Error {
2150 if (&V == TIDAddr || &V == ZeroAddr) {
2151 OI->ExcludeArgsFromAggregate.push_back(&V);
2152 return Error::success();
2153 }
2154
2156 for (Use &U : V.uses())
2157 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2158 if (ParallelRegionBlockSet.count(UserI->getParent()))
2159 Uses.insert(&U);
2160
2161 // __kmpc_fork_call expects extra arguments as pointers. If the input
2162 // already has a pointer type, everything is fine. Otherwise, store the
2163 // value onto stack and load it back inside the to-be-outlined region. This
2164 // will ensure only the pointer will be passed to the function.
2165 // FIXME: if there are more than 15 trailing arguments, they must be
2166 // additionally packed in a struct.
2167 Value *Inner = &V;
2168 if (!V.getType()->isPointerTy()) {
2170 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2171
2172 Builder.restoreIP(OuterAllocIP);
2173 Value *Ptr;
2174 if (UsesDeviceSharedMemory) {
2175 // Use device shared memory instead, if needed.
2176 Ptr = createOMPAllocShared(OuterAllocIP, V.getType(),
2177 V.getName() + ".reloaded");
2178 for (BasicBlock *DeallocBlock : OuterDeallocBlocks)
2180 InsertPointTy(DeallocBlock, DeallocBlock->getFirstInsertionPt()),
2181 Ptr, V.getType());
2182 } else {
2183 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2184 V.getName() + ".reloaded");
2185 }
2186
2187 // Store to stack at end of the block that currently branches to the entry
2188 // block of the to-be-outlined region.
2189 Builder.SetInsertPoint(InsertBB,
2190 InsertBB->getTerminator()->getIterator());
2191 Builder.CreateStore(&V, Ptr);
2192
2193 // Load back next to allocations in the to-be-outlined region.
2194 Builder.restoreIP(InnerAllocaIP);
2195 Inner = Builder.CreateLoad(V.getType(), Ptr);
2196 }
2197
2198 Value *ReplacementValue = nullptr;
2199 CallInst *CI = dyn_cast<CallInst>(&V);
2200 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2201 ReplacementValue = PrivTID;
2202 } else {
2203 InsertPointOrErrorTy AfterIP =
2204 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2205 if (!AfterIP)
2206 return AfterIP.takeError();
2207 Builder.restoreIP(*AfterIP);
2208 InnerAllocaIP = {
2209 InnerAllocaIP.getBlock(),
2210 InnerAllocaIP.getBlock()->getTerminator()->getIterator()};
2211
2212 assert(ReplacementValue &&
2213 "Expected copy/create callback to set replacement value!");
2214 if (ReplacementValue == &V)
2215 return Error::success();
2216 }
2217
2218 for (Use *UPtr : Uses)
2219 UPtr->set(ReplacementValue);
2220
2221 return Error::success();
2222 };
2223
2224 // Reset the inner alloca insertion as it will be used for loading the values
2225 // wrapped into pointers before passing them into the to-be-outlined region.
2226 // Configure it to insert immediately after the fake use of zero address so
2227 // that they are available in the generated body and so that the
2228 // OpenMP-related values (thread ID and zero address pointers) remain leading
2229 // in the argument list.
2230 InnerAllocaIP = IRBuilder<>::InsertPoint(
2231 ZeroAddrUse->getParent(), ZeroAddrUse->getNextNode()->getIterator());
2232
2233 // Reset the outer alloca insertion point to the entry of the relevant block
2234 // in case it was invalidated.
2235 OuterAllocIP = IRBuilder<>::InsertPoint(
2236 OuterAllocaBlock, OuterAllocaBlock->getFirstInsertionPt());
2237
2238 for (Value *Input : Inputs) {
2239 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2240 if (Error Err = PrivHelper(*Input))
2241 return Err;
2242 }
2243 LLVM_DEBUG({
2244 for (Value *Output : Outputs)
2245 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2246 });
2247 assert(Outputs.empty() &&
2248 "OpenMP outlining should not produce live-out values!");
2249
2250 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2251 LLVM_DEBUG({
2252 for (auto *BB : Blocks)
2253 dbgs() << " PBR: " << BB->getName() << "\n";
2254 });
2255
2256 // Adjust the finalization stack, verify the adjustment, and call the
2257 // finalize function a last time to finalize values between the pre-fini
2258 // block and the exit block if we left the parallel "the normal way".
2259 auto FiniInfo = FinalizationStack.pop_back_val();
2260 (void)FiniInfo;
2261 assert(FiniInfo.DK == OMPD_parallel &&
2262 "Unexpected finalization stack state!");
2263
2264 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2265
2266 InsertPointTy PreFiniIP(PRegPreFiniBB, PRegPreFiniTI->getIterator());
2267 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2268 if (!FiniBBOrErr)
2269 return FiniBBOrErr.takeError();
2270 {
2272 Builder.restoreIP(PreFiniIP);
2273 Builder.CreateBr(*FiniBBOrErr);
2274 // There's currently a branch to omp.par.exit. Delete it. We will get there
2275 // via the fini block
2276 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2277 Term->eraseFromParent();
2278 }
2279
2280 // Register the outlined info.
2281 addOutlineInfo(std::move(OI));
2282
2283 InsertPointTy AfterIP(UI->getParent(), UI->getParent()->end());
2284 UI->eraseFromParent();
2285
2286 return AfterIP;
2287}
2288
2290 // Build call void __kmpc_flush(ident_t *loc)
2291 uint32_t SrcLocStrSize;
2292 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2293 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2294
2296 Args);
2297}
2298
2300 if (!updateToLocation(Loc))
2301 return;
2302 emitFlush(Loc);
2303}
2304
2306 Value *Message) {
2307 if (!updateToLocation(Loc))
2308 return;
2309
2310 // Build call void __kmpc_error(ident_t *loc, int severity,
2311 // const char *message)
2312 uint32_t SrcLocStrSize;
2313 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2314 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2315 // Severity: 1 = warning, 2 = fatal.
2316 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2317 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2318 Value *Args[] = {Ident, Severity, MessageArg};
2319
2321 Args);
2322}
2323
2325 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2326 uint32_t SrcLocStrSize;
2327 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2328 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2329 Constant *I32Null = ConstantInt::getNullValue(Int32);
2330 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2331
2333 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2334}
2335
2341
2343 const DependData &Dep) {
2344 // Store the pointer to the variable
2345 Value *Addr = Builder.CreateStructGEP(
2346 DependInfo, Entry,
2347 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2348 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2349 Builder.CreateStore(DepValPtr, Addr);
2350 // Store the size of the variable
2351 Value *Size = Builder.CreateStructGEP(
2352 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2353 Builder.CreateStore(
2354 ConstantInt::get(SizeTy,
2355 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2356 Size);
2357 // Store the dependency kind
2358 Value *Flags = Builder.CreateStructGEP(
2359 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2360 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2361 static_cast<unsigned int>(Dep.DepKind)),
2362 Flags);
2363}
2364
2365// Processes the dependencies in Dependencies and does the following
2366// - Allocates space on the stack of an array of DependInfo objects
2367// - Populates each DependInfo object with relevant information of
2368// the corresponding dependence.
2369// - All code is inserted in the entry block of the current function.
2371 OpenMPIRBuilder &OMPBuilder,
2373 // Early return if we have no dependencies to process
2374 if (Dependencies.empty())
2375 return nullptr;
2376
2377 // Given a vector of DependData objects, in this function we create an
2378 // array on the stack that holds kmp_depend_info objects corresponding
2379 // to each dependency. This is then passed to the OpenMP runtime.
2380 // For example, if there are 'n' dependencies then the following psedo
2381 // code is generated. Assume the first dependence is on a variable 'a'
2382 //
2383 // \code{c}
2384 // DepArray = alloc(n x sizeof(kmp_depend_info);
2385 // idx = 0;
2386 // DepArray[idx].base_addr = ptrtoint(&a);
2387 // DepArray[idx].len = 8;
2388 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2389 // ++idx;
2390 // DepArray[idx].base_addr = ...;
2391 // \endcode
2392
2393 IRBuilderBase &Builder = OMPBuilder.Builder;
2394 Type *DependInfo = OMPBuilder.DependInfo;
2395
2396 Value *DepArray = nullptr;
2397 OpenMPIRBuilder::InsertPointTy OldIP = Builder.saveIP();
2398 Builder.SetInsertPoint(
2400
2401 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2402 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2403
2404 Builder.restoreIP(OldIP);
2405
2406 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2407 Value *Base =
2408 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2409 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2410 }
2411 return DepArray;
2412}
2413
2415 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2416 // global_tid);
2417 uint32_t SrcLocStrSize;
2418 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2419 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2420 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2421
2422 // Ignore return result until untied tasks are supported.
2424 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2425}
2426
2428 DependenciesInfo Dependencies) {
2429 if (!updateToLocation(Loc))
2430 return;
2431
2432 Value *DepArray = nullptr;
2433 Type *DepArrayTy = nullptr;
2434 Value *NumDeps = nullptr;
2435 if (Dependencies.DepArray) {
2436 DepArray = Dependencies.DepArray;
2437 NumDeps = Dependencies.NumDeps;
2438 } else if (!Dependencies.Deps.empty()) {
2439 InsertPointTy OldIP = Builder.saveIP();
2440 BasicBlock &entryBB =
2441 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2442 Builder.SetInsertPoint(&entryBB, entryBB.getFirstInsertionPt());
2443
2444 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2445 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2446 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2447
2448 Builder.restoreIP(OldIP);
2449 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2450 Value *Base =
2451 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2452 this->emitTaskDependency(Builder, Base, Dep);
2453 }
2454 }
2455
2456 if (DepArray) {
2457 uint32_t SrcLocStrSize;
2458 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2459 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2460 Value *Args[] = {
2461 Ident,
2462 getOrCreateThreadID(Ident),
2463 NumDeps,
2464 DepArray,
2465 ConstantInt::get(Builder.getInt32Ty(), 0),
2467 ConstantInt::get(Builder.getInt32Ty(), false)};
2470 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2471 Args);
2472 } else {
2474 }
2475}
2476
2477/// Create the task duplication function passed to kmpc_taskloop.
2478Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2479 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2480 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2481 if (!DupCB)
2483 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2484
2485 // From OpenMP Runtime p_task_dup_t:
2486 // Routine optionally generated by the compiler for setting the lastprivate
2487 // flag and calling needed constructors for private/firstprivate objects (used
2488 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2489 // lastprivate flag.
2490 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2491
2492 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2493
2494 FunctionType *DupFuncTy = FunctionType::get(
2495 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2496 /*isVarArg=*/false);
2497
2498 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2499 "omp_taskloop_dup", M);
2500 Value *DestTaskArg = DupFunction->getArg(0);
2501 Value *SrcTaskArg = DupFunction->getArg(1);
2502 Value *LastprivateFlagArg = DupFunction->getArg(2);
2503 DestTaskArg->setName("dest_task");
2504 SrcTaskArg->setName("src_task");
2505 LastprivateFlagArg->setName("lastprivate_flag");
2506
2507 IRBuilderBase::InsertPointGuard Guard(Builder);
2508 Builder.SetInsertPoint(
2509 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2510
2511 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2512 Type *TaskWithPrivatesTy =
2513 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2514 Value *TaskPrivates = Builder.CreateGEP(
2515 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2516 Value *ContextPtr = Builder.CreateGEP(
2517 PrivatesTy, TaskPrivates,
2518 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2519 return ContextPtr;
2520 };
2521
2522 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2523 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2524
2525 DestTaskContextPtr->setName("destPtr");
2526 SrcTaskContextPtr->setName("srcPtr");
2527
2528 InsertPointTy AllocaIP(&DupFunction->getEntryBlock(),
2529 DupFunction->getEntryBlock().begin());
2530 InsertPointTy CodeGenIP = Builder.saveIP();
2531 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2532 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2533 if (!AfterIPOrError)
2534 return AfterIPOrError.takeError();
2535 Builder.restoreIP(*AfterIPOrError);
2536
2537 Builder.CreateRetVoid();
2538
2539 return DupFunction;
2540}
2541
2542OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2543 const LocationDescription &Loc, InsertPointTy AllocaIP,
2544 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2545 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2546 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2547 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2548 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2549 Value *TaskContextStructPtrVal) {
2550
2551 if (!updateToLocation(Loc))
2552 return InsertPointTy();
2553
2554 uint32_t SrcLocStrSize;
2555 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2556 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2557
2558 BasicBlock *TaskloopExitBB =
2559 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2560 BasicBlock *TaskloopBodyBB =
2561 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2562 BasicBlock *TaskloopAllocaBB =
2563 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2564
2565 InsertPointTy TaskloopAllocaIP =
2566 InsertPointTy(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2567 InsertPointTy TaskloopBodyIP =
2568 InsertPointTy(TaskloopBodyBB, TaskloopBodyBB->begin());
2569
2570 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2571 return Err;
2572
2573 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2574 if (!result) {
2575 return result.takeError();
2576 }
2577
2578 llvm::CanonicalLoopInfo *CLI = result.get();
2579 auto OI = std::make_unique<OutlineInfo>();
2580 OI->EntryBB = TaskloopAllocaBB;
2581 OI->OuterAllocBB = AllocaIP.getBlock();
2582 OI->ExitBB = TaskloopExitBB;
2583 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2584 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2585
2586 // Add the thread ID argument.
2587 SmallVector<Instruction *> ToBeDeleted;
2588 // dummy instruction to be used as a fake argument
2589 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2590 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2591 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2592 TaskloopAllocaIP, "lb", false, true);
2593 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2594 TaskloopAllocaIP, "ub", false, true);
2595 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2596 TaskloopAllocaIP, "step", false, true);
2597 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2598 // aggregate struct
2599 OI->Inputs.insert(FakeLB);
2600 OI->Inputs.insert(FakeUB);
2601 OI->Inputs.insert(FakeStep);
2602 if (TaskContextStructPtrVal)
2603 OI->Inputs.insert(TaskContextStructPtrVal);
2604 assert(((TaskContextStructPtrVal && DupCB) ||
2605 (!TaskContextStructPtrVal && !DupCB)) &&
2606 "Task context struct ptr and duplication callback must be both set "
2607 "or both null");
2608
2609 // It isn't safe to run the duplication bodygen callback inside the post
2610 // outlining callback so this has to be run now before we know the real task
2611 // shareds structure type.
2612 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2613 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2614 Type *FakeSharedsTy = StructType::get(
2615 Builder.getContext(),
2616 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2617 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2618 FakeSharedsTy,
2619 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2620 if (!TaskDupFnOrErr) {
2621 return TaskDupFnOrErr.takeError();
2622 }
2623 Value *TaskDupFn = *TaskDupFnOrErr;
2624
2625 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2626 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2627 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2628 FakeSharedsTy, Final, Mergeable, Priority,
2629 NumOfCollapseLoops](Function &OutlinedFn) mutable {
2630 // Replace the Stale CI by appropriate RTL function call.
2631 assert(OutlinedFn.hasOneUse() &&
2632 "there must be a single user for the outlined function");
2633 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2634
2635 /* Create the casting for the Bounds Values that can be used when outlining
2636 * to replace the uses of the fakes with real values */
2637 BasicBlock *CodeReplBB = StaleCI->getParent();
2638 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2639 Value *CastedLBVal =
2640 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2641 Value *CastedUBVal =
2642 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2643 Value *CastedStepVal =
2644 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2645
2646 Builder.SetInsertPoint(StaleCI);
2647
2648 // Gather the arguments for emitting the runtime call for
2649 // @__kmpc_omp_task_alloc
2650 Function *TaskAllocFn =
2651 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2652
2653 Value *ThreadID = getOrCreateThreadID(Ident);
2654
2655 if (!NoGroup) {
2656 // Emit runtime call for @__kmpc_taskgroup
2657 Function *TaskgroupFn =
2658 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2659 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2660 }
2661
2662 // `flags` Argument Configuration
2663 // Task is tied if (Flags & 1) == 1.
2664 // Task is untied if (Flags & 1) == 0.
2665 // Task is final if (Flags & 2) == 2.
2666 // Task is not final if (Flags & 2) == 0.
2667 // Task is mergeable if (Flags & 4) == 4.
2668 // Task is not mergeable if (Flags & 4) == 0.
2669 // Task is priority if (Flags & 32) == 32.
2670 // Task is not priority if (Flags & 32) == 0.
2671 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2672 if (Final)
2673 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2674 if (Mergeable)
2675 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2676 if (Priority)
2677 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2678
2679 Value *TaskSize = Builder.getInt64(
2680 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2681
2682 AllocaInst *ArgStructAlloca =
2684 assert(ArgStructAlloca &&
2685 "Unable to find the alloca instruction corresponding to arguments "
2686 "for extracted function");
2687 std::optional<TypeSize> ArgAllocSize =
2688 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2689 assert(ArgAllocSize &&
2690 "Unable to determine size of arguments for extracted function");
2691 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2692
2693 // Emit the @__kmpc_omp_task_alloc runtime call
2694 // The runtime call returns a pointer to an area where the task captured
2695 // variables must be copied before the task is run (TaskData)
2696 CallInst *TaskData = Builder.CreateCall(
2697 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2698 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2699 /*task_func=*/&OutlinedFn});
2700
2701 Value *Shareds = StaleCI->getArgOperand(1);
2702 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2703 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2704 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2705 SharedsSize);
2706 // Get the pointer to loop lb, ub, step from task ptr
2707 // and set up the lowerbound,upperbound and step values
2708 llvm::Value *Lb = Builder.CreateGEP(
2709 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2710
2711 llvm::Value *Ub = Builder.CreateGEP(
2712 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2713
2714 llvm::Value *Step = Builder.CreateGEP(
2715 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2716 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2717
2718 // set up the arguments for emitting kmpc_taskloop runtime call
2719 // setting values for ifval, nogroup, sched, grainsize, task_dup
2720 Value *IfCondVal =
2721 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2722 : Builder.getInt32(1);
2723 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2724 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2725 Value *NoGroupVal = Builder.getInt32(1);
2726 Value *SchedVal = Builder.getInt32(Sched);
2727 Value *GrainSizeVal =
2728 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2729 : Builder.getInt64(0);
2730 Value *TaskDup = TaskDupFn;
2731
2732 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2733 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2734
2735 // taskloop runtime call
2736 Function *TaskloopFn =
2737 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2738 Builder.CreateCall(TaskloopFn, Args);
2739
2740 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2741 // nogroup is not defined
2742 if (!NoGroup) {
2743 Function *EndTaskgroupFn =
2744 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2745 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2746 }
2747
2748 StaleCI->eraseFromParent();
2749
2750 Builder.SetInsertPoint(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2751
2752 LoadInst *SharedsOutlined =
2753 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2754 OutlinedFn.getArg(1)->replaceUsesWithIf(
2755 SharedsOutlined,
2756 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2757
2758 Value *IV = CLI->getIndVar();
2759 Type *IVTy = IV->getType();
2760 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2761
2762 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2763 // UpperBound. These GEP's can be reused for loading the tasks respective
2764 // bounds.
2765 Value *TaskLB = nullptr;
2766 Value *TaskUB = nullptr;
2767 Value *TaskStep = nullptr;
2768 Value *LoadTaskLB = nullptr;
2769 Value *LoadTaskUB = nullptr;
2770 Value *LoadTaskStep = nullptr;
2771 for (Instruction &I : *TaskloopAllocaBB) {
2772 if (I.getOpcode() == Instruction::GetElementPtr) {
2773 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2774 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2775 switch (CI->getZExtValue()) {
2776 case 0:
2777 TaskLB = &I;
2778 break;
2779 case 1:
2780 TaskUB = &I;
2781 break;
2782 case 2:
2783 TaskStep = &I;
2784 break;
2785 }
2786 }
2787 } else if (I.getOpcode() == Instruction::Load) {
2788 LoadInst &Load = cast<LoadInst>(I);
2789 if (Load.getPointerOperand() == TaskLB) {
2790 assert(TaskLB != nullptr && "Expected value for TaskLB");
2791 LoadTaskLB = &I;
2792 } else if (Load.getPointerOperand() == TaskUB) {
2793 assert(TaskUB != nullptr && "Expected value for TaskUB");
2794 LoadTaskUB = &I;
2795 } else if (Load.getPointerOperand() == TaskStep) {
2796 assert(TaskStep != nullptr && "Expected value for TaskStep");
2797 LoadTaskStep = &I;
2798 }
2799 }
2800 }
2801
2802 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2803
2804 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2805 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2806 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2807 Value *TripCountMinusOne = Builder.CreateSDiv(
2808 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2809 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2810 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2811 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2812 // set the trip count in the CLI
2813 CLI->setTripCount(CastedTripCount);
2814
2815 Builder.SetInsertPoint(CLI->getBody(),
2816 CLI->getBody()->getFirstInsertionPt());
2817
2818 if (NumOfCollapseLoops > 1) {
2819 llvm::SmallVector<User *> UsersToReplace;
2820 // When using the collapse clause, the bounds of the loop have to be
2821 // adjusted to properly represent the iterator of the outer loop.
2822 Value *IVPlusTaskLB = Builder.CreateAdd(
2823 CLI->getIndVar(),
2824 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2825 // To ensure every Use is correctly captured, we first want to record
2826 // which users to replace the value in, and then replace the value.
2827 for (auto IVUse = CLI->getIndVar()->uses().begin();
2828 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2829 User *IVUser = IVUse->getUser();
2830 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2831 if (Op->getOpcode() == Instruction::URem ||
2832 Op->getOpcode() == Instruction::UDiv) {
2833 UsersToReplace.push_back(IVUser);
2834 }
2835 }
2836 }
2837 for (User *User : UsersToReplace) {
2838 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2839 }
2840 } else {
2841 // The canonical loop is generated with a fixed lower bound. We need to
2842 // update the index calculation code to use the task's lower bound. The
2843 // generated code looks like this:
2844 // %omp_loop.iv = phi ...
2845 // ...
2846 // %tmp = mul [type] %omp_loop.iv, step
2847 // %user_index = add [type] tmp, lb
2848 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2849 // of the normalised induction variable:
2850 // 1. This one: converting the normalised IV to the user IV
2851 // 2. The increment (add)
2852 // 3. The comparison against the trip count (icmp)
2853 // (1) is the only use that is a mul followed by an add so this cannot
2854 // match other IR.
2855 assert(CLI->getIndVar()->getNumUses() == 3 &&
2856 "Canonical loop should have exactly three uses of the ind var");
2857 for (User *IVUser : CLI->getIndVar()->users()) {
2858 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2859 if (Mul->getOpcode() == Instruction::Mul) {
2860 for (User *MulUser : Mul->users()) {
2861 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2862 if (Add->getOpcode() == Instruction::Add) {
2863 Add->setOperand(1, CastedTaskLB);
2864 }
2865 }
2866 }
2867 }
2868 }
2869 }
2870 }
2871
2872 FakeLB->replaceAllUsesWith(CastedLBVal);
2873 FakeUB->replaceAllUsesWith(CastedUBVal);
2874 FakeStep->replaceAllUsesWith(CastedStepVal);
2875 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2876 I->eraseFromParent();
2877 }
2878 };
2879
2880 addOutlineInfo(std::move(OI));
2881 Builder.SetInsertPoint(TaskloopExitBB, TaskloopExitBB->begin());
2882 return Builder.saveIP();
2883}
2884
2887 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2889 llvm::Type::getInt32Ty(M.getContext()));
2890}
2891
2893 const LocationDescription &Loc, InsertPointTy AllocaIP,
2894 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2895 bool Tied, Value *Final, Value *IfCondition,
2896 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2897 bool Mergeable, Value *EventHandle, Value *Priority) {
2898
2899 if (!updateToLocation(Loc))
2900 return InsertPointTy();
2901
2902 uint32_t SrcLocStrSize;
2903 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2904 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2905 // The current basic block is split into four basic blocks. After outlining,
2906 // they will be mapped as follows:
2907 // ```
2908 // def current_fn() {
2909 // current_basic_block:
2910 // br label %task.exit
2911 // task.exit:
2912 // ; instructions after task
2913 // }
2914 // def outlined_fn() {
2915 // task.alloca:
2916 // br label %task.body
2917 // task.body:
2918 // ret void
2919 // }
2920 // ```
2921 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2922 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2923 BasicBlock *TaskAllocaBB =
2924 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2925
2926 InsertPointTy TaskAllocaIP =
2927 InsertPointTy(TaskAllocaBB, TaskAllocaBB->begin());
2928 InsertPointTy TaskBodyIP = InsertPointTy(TaskBodyBB, TaskBodyBB->begin());
2929 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2930 return Err;
2931
2932 auto OI = std::make_unique<OutlineInfo>();
2933 OI->EntryBB = TaskAllocaBB;
2934 OI->OuterAllocBB = AllocaIP.getBlock();
2935 OI->ExitBB = TaskExitBB;
2936 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2937 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2938
2939 // Add the thread ID argument.
2941 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2942 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2943
2944 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2945 Affinities, Mergeable, Priority, EventHandle,
2946 TaskAllocaBB,
2947 ToBeDeleted](Function &OutlinedFn) mutable {
2948 // Replace the Stale CI by appropriate RTL function call.
2949 assert(OutlinedFn.hasOneUse() &&
2950 "there must be a single user for the outlined function");
2951 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2952
2953 // HasShareds is true if any variables are captured in the outlined region,
2954 // false otherwise.
2955 bool HasShareds = StaleCI->arg_size() > 1;
2956 Builder.SetInsertPoint(StaleCI);
2957
2958 // Gather the arguments for emitting the runtime call for
2959 // @__kmpc_omp_task_alloc
2960 Function *TaskAllocFn =
2961 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2962
2963 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
2964 // call.
2965 Value *ThreadID = getOrCreateThreadID(Ident);
2966
2967 // Argument - `flags`
2968 // Task is tied iff (Flags & 1) == 1.
2969 // Task is untied iff (Flags & 1) == 0.
2970 // Task is final iff (Flags & 2) == 2.
2971 // Task is not final iff (Flags & 2) == 0.
2972 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
2973 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
2974 // Task is detachable iff (Flags & 64) == 64.
2975 // Task is not detachable iff (Flags & 64) == 0.
2976 // Task is priority iff (Flags & 32) == 32.
2977 // Task is not priority iff (Flags & 32) == 0.
2978 // TODO: Handle the other flags.
2979 Value *Flags = Builder.getInt32(Tied);
2980 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
2981 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
2982 if (Final) {
2983 Value *FinalFlag =
2984 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
2985 Flags = Builder.CreateOr(FinalFlag, Flags);
2986 }
2987
2988 if (Mergeable || UseMergedIf0Path)
2989 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2990 if (EventHandle)
2991 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
2992 if (Priority)
2993 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2994
2995 // Argument - `sizeof_kmp_task_t` (TaskSize)
2996 // Tasksize refers to the size in bytes of kmp_task_t data structure
2997 // including private vars accessed in task.
2998 // TODO: add kmp_task_t_with_privates (privates)
2999 Value *TaskSize = Builder.getInt64(
3000 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3001
3002 // Argument - `sizeof_shareds` (SharedsSize)
3003 // SharedsSize refers to the shareds array size in the kmp_task_t data
3004 // structure.
3005 Value *SharedsSize = Builder.getInt64(0);
3006 if (HasShareds) {
3007 AllocaInst *ArgStructAlloca =
3009 assert(ArgStructAlloca &&
3010 "Unable to find the alloca instruction corresponding to arguments "
3011 "for extracted function");
3012 std::optional<TypeSize> ArgAllocSize =
3013 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3014 assert(ArgAllocSize &&
3015 "Unable to determine size of arguments for extracted function");
3016 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3017 }
3018 // Emit the @__kmpc_omp_task_alloc runtime call
3019 // The runtime call returns a pointer to an area where the task captured
3020 // variables must be copied before the task is run (TaskData)
3022 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3023 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3024 /*task_func=*/&OutlinedFn});
3025
3026 if (Affinities.Count && Affinities.Info) {
3028 OMPRTL___kmpc_omp_reg_task_with_affinity);
3029
3030 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3031 Affinities.Count, Affinities.Info});
3032 }
3033
3034 // Emit detach clause initialization.
3035 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3036 // task_descriptor);
3037 if (EventHandle) {
3039 OMPRTL___kmpc_task_allow_completion_event);
3040 llvm::Value *EventVal =
3041 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3042 llvm::Value *EventHandleAddr =
3043 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3044 Builder.getPtrTy(0));
3045 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3046 Builder.CreateStore(EventVal, EventHandleAddr);
3047 }
3048 // Copy the arguments for outlined function
3049 if (HasShareds) {
3050 Value *Shareds = StaleCI->getArgOperand(1);
3051 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3052 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3053 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3054 SharedsSize);
3055 }
3056
3057 if (Priority) {
3058 //
3059 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3060 // we populate the priority information into the "kmp_task_t" here
3061 //
3062 // The struct "kmp_task_t" definition is available in kmp.h
3063 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3064 // data2 is used for priority
3065 //
3066 Type *Int32Ty = Builder.getInt32Ty();
3067 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3068 // kmp_task_t* => { ptr }
3069 Type *TaskPtr = StructType::get(VoidPtr);
3070 Value *TaskGEP =
3071 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3072 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3073 Type *TaskStructType = StructType::get(
3074 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3075 Value *PriorityData = Builder.CreateInBoundsGEP(
3076 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3077 // kmp_cmplrdata_t => { ptr, ptr }
3078 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3079 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3080 PriorityData, {Zero, Zero});
3081 Builder.CreateStore(Priority, CmplrData);
3082 }
3083
3084 Value *DepArray = nullptr;
3085 Value *NumDeps = nullptr;
3086 if (Dependencies.DepArray) {
3087 DepArray = Dependencies.DepArray;
3088 NumDeps = Dependencies.NumDeps;
3089 } else if (!Dependencies.Deps.empty()) {
3090 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3091 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3092 }
3093
3094 // In the presence of the `if` clause, the following IR is generated:
3095 // ...
3096 // %data = call @__kmpc_omp_task_alloc(...)
3097 // br i1 %if_condition, label %then, label %else
3098 // then:
3099 // call @__kmpc_omp_task(...)
3100 // br label %exit
3101 // else:
3102 // ;; Wait for resolution of dependencies, if any, before
3103 // ;; beginning the task
3104 // call @__kmpc_omp_wait_deps(...)
3105 // call @__kmpc_omp_task_begin_if0(...)
3106 // call @outlined_fn(...)
3107 // call @__kmpc_omp_task_complete_if0(...)
3108 // br label %exit
3109 // exit:
3110 // ...
3111 if (IfCondition && !UseMergedIf0Path) {
3112 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3113 // terminator.
3114 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3115 Instruction *IfTerminator =
3116 Builder.GetInsertPoint()->getParent()->getTerminator();
3117 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3118 Builder.SetInsertPoint(IfTerminator);
3119 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3120 &ElseTI);
3121 Builder.SetInsertPoint(ElseTI);
3122
3123 if (DepArray) {
3124 Function *TaskWaitFn =
3125 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3127 TaskWaitFn,
3128 {Ident, ThreadID, NumDeps, DepArray,
3129 ConstantInt::get(Builder.getInt32Ty(), 0),
3131 }
3132 Function *TaskBeginFn =
3133 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3134 Function *TaskCompleteFn =
3135 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3136 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3137 CallInst *CI = nullptr;
3138 if (HasShareds)
3139 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3140 else
3141 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3142 CI->setDebugLoc(StaleCI->getDebugLoc());
3143 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3144 Builder.SetInsertPoint(ThenTI);
3145 }
3146
3147 if (DepArray) {
3148 Function *TaskFn =
3149 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3151 TaskFn,
3152 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3153 ConstantInt::get(Builder.getInt32Ty(), 0),
3155
3156 } else {
3157 // Emit the @__kmpc_omp_task runtime call to spawn the task
3158 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3159 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3160 }
3161
3162 StaleCI->eraseFromParent();
3163
3164 Builder.SetInsertPoint(TaskAllocaBB, TaskAllocaBB->begin());
3165 if (HasShareds) {
3166 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3167 OutlinedFn.getArg(1)->replaceUsesWithIf(
3168 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3169 }
3170
3171 for (Instruction *I : llvm::reverse(ToBeDeleted))
3172 I->eraseFromParent();
3173 };
3174
3175 addOutlineInfo(std::move(OI));
3176 Builder.SetInsertPoint(TaskExitBB, TaskExitBB->begin());
3177
3178 return Builder.saveIP();
3179}
3180
3182 const LocationDescription &Loc, InsertPointTy AllocaIP,
3183 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3184 if (!updateToLocation(Loc))
3185 return InsertPointTy();
3186
3187 uint32_t SrcLocStrSize;
3188 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3189 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3190 Value *ThreadID = getOrCreateThreadID(Ident);
3191
3192 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3193 Function *TaskgroupFn =
3194 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3195 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3196
3197 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3198 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3199 return Err;
3200
3201 Builder.SetInsertPoint(TaskgroupExitBB);
3202 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3203 Function *EndTaskgroupFn =
3204 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3205 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3206
3207 return Builder.saveIP();
3208}
3209
3211 const LocationDescription &Loc, InsertPointTy AllocaIP,
3213 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3214 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3215
3216 if (!updateToLocation(Loc))
3217 return Loc.IP;
3218
3219 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3220
3221 // Each section is emitted as a switch case
3222 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3223 // -> OMP.createSection() which generates the IR for each section
3224 // Iterate through all sections and emit a switch construct:
3225 // switch (IV) {
3226 // case 0:
3227 // <SectionStmt[0]>;
3228 // break;
3229 // ...
3230 // case <NumSection> - 1:
3231 // <SectionStmt[<NumSection> - 1]>;
3232 // break;
3233 // }
3234 // ...
3235 // section_loop.after:
3236 // <FiniCB>;
3237 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3238 Builder.restoreIP(CodeGenIP);
3240 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3241 Function *CurFn = Continue->getParent();
3242 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3243
3244 unsigned CaseNumber = 0;
3245 for (auto SectionCB : SectionCBs) {
3247 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3248 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3249 Builder.SetInsertPoint(CaseBB);
3250 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3251 if (Error Err =
3252 SectionCB(InsertPointTy(),
3253 {CaseEndBr->getParent(), CaseEndBr->getIterator()}, {}))
3254 return Err;
3255 CaseNumber++;
3256 }
3257 // remove the existing terminator from body BB since there can be no
3258 // terminators after switch/case
3259 return Error::success();
3260 };
3261 // Loop body ends here
3262 // LowerBound, UpperBound, and STride for createCanonicalLoop
3263 Type *I32Ty = Type::getInt32Ty(M.getContext());
3264 Value *LB = ConstantInt::get(I32Ty, 0);
3265 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3266 Value *ST = ConstantInt::get(I32Ty, 1);
3268 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3269 if (!LoopInfo)
3270 return LoopInfo.takeError();
3271
3272 InsertPointOrErrorTy WsloopIP =
3273 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3274 WorksharingLoopType::ForStaticLoop, !IsNowait);
3275 if (!WsloopIP)
3276 return WsloopIP.takeError();
3277 InsertPointTy AfterIP = *WsloopIP;
3278
3279 BasicBlock *LoopFini = AfterIP.getBlock()->getSinglePredecessor();
3280 assert(LoopFini && "Bad structure of static workshare loop finalization");
3281
3282 // Apply the finalization callback in LoopAfterBB
3283 auto FiniInfo = FinalizationStack.pop_back_val();
3284 assert(FiniInfo.DK == OMPD_sections &&
3285 "Unexpected finalization stack state!");
3286 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3287 return Err;
3288
3289 return AfterIP;
3290}
3291
3294 BodyGenCallbackTy BodyGenCB,
3295 FinalizeCallbackTy FiniCB) {
3296 if (!updateToLocation(Loc))
3297 return Loc.IP;
3298
3299 auto FiniCBWrapper = [&](InsertPointTy IP) {
3300 if (IP.getBlock()->end() != IP.getPoint())
3301 return FiniCB(IP);
3302 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3303 // will fail because that function requires the Finalization Basic Block to
3304 // have a terminator, which is already removed by EmitOMPRegionBody.
3305 // IP is currently at cancelation block.
3306 // We need to backtrack to the condition block to fetch
3307 // the exit block and create a branch from cancelation
3308 // to exit block.
3310 Builder.restoreIP(IP);
3311 auto *CaseBB = Loc.IP.getBlock();
3312 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3313 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3314 Instruction *I = Builder.CreateBr(ExitBB);
3315 IP = InsertPointTy(I->getParent(), I->getIterator());
3316 return FiniCB(IP);
3317 };
3318
3319 Directive OMPD = Directive::OMPD_sections;
3320 // Since we are using Finalization Callback here, HasFinalize
3321 // and IsCancellable have to be true
3322 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3323 /*Conditional*/ false, /*hasFinalize*/ true,
3324 /*IsCancellable*/ true);
3325}
3326
3332
3333Value *OpenMPIRBuilder::getGPUThreadID() {
3336 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3337 {});
3338}
3339
3340Value *OpenMPIRBuilder::getGPUWarpSize() {
3342 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3343}
3344
3345Value *OpenMPIRBuilder::getNVPTXWarpID() {
3346 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3347 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3348}
3349
3350Value *OpenMPIRBuilder::getNVPTXLaneID() {
3351 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3352 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3353 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3354 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3355 "nvptx_lane_id");
3356}
3357
3358Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3359 Type *ToType) {
3360 Type *FromType = From->getType();
3361 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3362 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3363 assert(FromSize > 0 && "From size must be greater than zero");
3364 assert(ToSize > 0 && "To size must be greater than zero");
3365 if (FromType == ToType)
3366 return From;
3367 if (FromSize == ToSize)
3368 return Builder.CreateBitCast(From, ToType);
3369 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3370 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3371 InsertPointTy SaveIP = Builder.saveIP();
3372 Builder.restoreIP(AllocaIP);
3373 Value *CastItem = Builder.CreateAlloca(ToType);
3374 Builder.restoreIP(SaveIP);
3375
3376 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3377 CastItem, Builder.getPtrTy(0));
3378 Builder.CreateStore(From, ValCastItem);
3379 return Builder.CreateLoad(ToType, CastItem);
3380}
3381
3382Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3383 Value *Element,
3384 Type *ElementType,
3385 Value *Offset) {
3386 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3387 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3388
3389 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3390 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3391 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3392 Value *WarpSize =
3393 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3395 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3396 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3397 Value *WarpSizeCast =
3398 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3399 Value *ShuffleCall =
3400 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3401 return castValueToType(AllocaIP, ShuffleCall, CastTy);
3402}
3403
3404void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3405 Value *DstAddr, Type *ElemType,
3406 Value *Offset, Type *ReductionArrayTy,
3407 bool IsByRefElem) {
3408 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3409 // Create the loop over the big sized data.
3410 // ptr = (void*)Elem;
3411 // ptrEnd = (void*) Elem + 1;
3412 // Step = 8;
3413 // while (ptr + Step < ptrEnd)
3414 // shuffle((int64_t)*ptr);
3415 // Step = 4;
3416 // while (ptr + Step < ptrEnd)
3417 // shuffle((int32_t)*ptr);
3418 // ...
3419 Type *IndexTy = Builder.getIndexTy(
3420 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3421 Value *ElemPtr = DstAddr;
3422 Value *Ptr = SrcAddr;
3423 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3424 if (Size < IntSize)
3425 continue;
3426 Type *IntType = Builder.getIntNTy(IntSize * 8);
3427 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3428 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3429 Value *SrcAddrGEP =
3430 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3431 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3432 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3433
3434 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3435 if ((Size / IntSize) > 1) {
3436 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3437 SrcAddrGEP, Builder.getPtrTy());
3438 BasicBlock *PreCondBB =
3439 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3440 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3441 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3442 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3443 emitBlock(PreCondBB, CurFunc);
3444 PHINode *PhiSrc =
3445 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3446 PhiSrc->addIncoming(Ptr, CurrentBB);
3447 PHINode *PhiDest =
3448 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3449 PhiDest->addIncoming(ElemPtr, CurrentBB);
3450 Ptr = PhiSrc;
3451 ElemPtr = PhiDest;
3452 Value *PtrDiff = Builder.CreatePtrDiff(
3453 Builder.getInt8Ty(), PtrEnd,
3454 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3455 Builder.CreateCondBr(
3456 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3457 ExitBB);
3458 emitBlock(ThenBB, CurFunc);
3459 Value *Res = createRuntimeShuffleFunction(
3460 AllocaIP,
3461 Builder.CreateAlignedLoad(
3462 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3463 IntType, Offset);
3464 Builder.CreateAlignedStore(Res, ElemPtr,
3465 M.getDataLayout().getPrefTypeAlign(ElemType));
3466 Value *LocalPtr =
3467 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3468 Value *LocalElemPtr =
3469 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3470 PhiSrc->addIncoming(LocalPtr, ThenBB);
3471 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3472 emitBranch(PreCondBB);
3473 emitBlock(ExitBB, CurFunc);
3474 } else {
3475 Value *Res = createRuntimeShuffleFunction(
3476 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3477 if (ElemType->isIntegerTy() && ElemType->getScalarSizeInBits() <
3478 Res->getType()->getScalarSizeInBits())
3479 Res = Builder.CreateTrunc(Res, ElemType);
3480 Builder.CreateStore(Res, ElemPtr);
3481 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3482 ElemPtr =
3483 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3484 }
3485 Size = Size % IntSize;
3486 }
3487}
3488
3489Error OpenMPIRBuilder::emitReductionListCopy(
3490 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3491 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3492 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3493 Type *IndexTy = Builder.getIndexTy(
3494 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3495 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3496
3497 // Iterates, element-by-element, through the source Reduce list and
3498 // make a copy.
3499 for (auto En : enumerate(ReductionInfos)) {
3500 const ReductionInfo &RI = En.value();
3501 Value *SrcElementAddr = nullptr;
3502 AllocaInst *DestAlloca = nullptr;
3503 Value *DestElementAddr = nullptr;
3504 Value *DestElementPtrAddr = nullptr;
3505 // Should we shuffle in an element from a remote lane?
3506 bool ShuffleInElement = false;
3507 // Set to true to update the pointer in the dest Reduce list to a
3508 // newly created element.
3509 bool UpdateDestListPtr = false;
3510
3511 // Step 1.1: Get the address for the src element in the Reduce list.
3512 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3513 ReductionArrayTy, SrcBase,
3514 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3515 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3516
3517 // Step 1.2: Create a temporary to store the element in the destination
3518 // Reduce list.
3519 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3520 ReductionArrayTy, DestBase,
3521 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3522 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3523 switch (Action) {
3525 InsertPointTy CurIP = Builder.saveIP();
3526 Builder.restoreIP(AllocaIP);
3527
3528 Type *DestAllocaType =
3529 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3530 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3531 ".omp.reduction.element");
3532 DestAlloca->setAlignment(
3533 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3534 DestElementAddr = DestAlloca;
3535 DestElementAddr =
3536 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3537 DestElementAddr->getName() + ".ascast");
3538 Builder.restoreIP(CurIP);
3539 ShuffleInElement = true;
3540 UpdateDestListPtr = true;
3541 break;
3542 }
3544 DestElementAddr =
3545 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3546 break;
3547 }
3548 }
3549
3550 // Now that all active lanes have read the element in the
3551 // Reduce list, shuffle over the value from the remote lane.
3552 if (ShuffleInElement) {
3553 Type *ShuffleType = RI.ElementType;
3554 Value *ShuffleSrcAddr = SrcElementAddr;
3555 Value *ShuffleDestAddr = DestElementAddr;
3556 AllocaInst *LocalStorage = nullptr;
3557
3558 if (IsByRefElem) {
3559 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3560 assert(RI.ByRefAllocatedType &&
3561 "Expected by-ref allocated type to be set");
3562 // For by-ref reductions, we need to copy from the remote lane the
3563 // actual value of the partial reduction computed by that remote lane;
3564 // rather than, for example, a pointer to that data or, even worse, a
3565 // pointer to the descriptor of the by-ref reduction element.
3566 ShuffleType = RI.ByRefElementType;
3567
3568 if (RI.DataPtrPtrGen) {
3569 // Descriptor-based by-ref: extract data pointer from descriptor.
3570 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3571 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3572
3573 if (!GenResult)
3574 return GenResult.takeError();
3575
3576 ShuffleSrcAddr =
3577 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3578
3579 {
3580 InsertPointTy OldIP = Builder.saveIP();
3581 Builder.restoreIP(AllocaIP);
3582
3583 LocalStorage = Builder.CreateAlloca(ShuffleType);
3584 Builder.restoreIP(OldIP);
3585 ShuffleDestAddr = LocalStorage;
3586 }
3587 } else {
3588 // Non-descriptor by-ref: the pointer already references data
3589 // directly. Shuffle into the destination alloca.
3590 ShuffleDestAddr = DestElementAddr;
3591 }
3592 }
3593
3594 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3595 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3596
3597 if (IsByRefElem && RI.DataPtrPtrGen) {
3598 // Copy descriptor from source and update base_ptr to shuffled data
3599 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3600 DestAlloca, Builder.getPtrTy(), ".ascast");
3601
3602 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3603 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3604 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3605
3606 if (!GenResult)
3607 return GenResult.takeError();
3608 }
3609 } else {
3610 switch (RI.EvaluationKind) {
3611 case EvalKind::Scalar: {
3612 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3613 // Store the source element value to the dest element address.
3614 Builder.CreateStore(Elem, DestElementAddr);
3615 break;
3616 }
3617 case EvalKind::Complex: {
3618 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3619 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3620 Value *SrcReal = Builder.CreateLoad(
3621 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3622 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3623 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3624 Value *SrcImg = Builder.CreateLoad(
3625 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3626
3627 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3628 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3629 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3630 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3631 Builder.CreateStore(SrcReal, DestRealPtr);
3632 Builder.CreateStore(SrcImg, DestImgPtr);
3633 break;
3634 }
3635 case EvalKind::Aggregate: {
3636 Value *SizeVal = Builder.getInt64(
3637 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3638 Builder.CreateMemCpy(
3639 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3640 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3641 SizeVal, false);
3642 break;
3643 }
3644 };
3645 }
3646
3647 // Step 3.1: Modify reference in dest Reduce list as needed.
3648 // Modifying the reference in Reduce list to point to the newly
3649 // created element. The element is live in the current function
3650 // scope and that of functions it invokes (i.e., reduce_function).
3651 // RemoteReduceData[i] = (void*)&RemoteElem
3652 if (UpdateDestListPtr) {
3653 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3654 DestElementAddr, Builder.getPtrTy(),
3655 DestElementAddr->getName() + ".ascast");
3656 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3657 }
3658 }
3659
3660 return Error::success();
3661}
3662
3663Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3664 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3665 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3666 IRBuilder<>::InsertPointGuard IPG(Builder);
3667 LLVMContext &Ctx = M.getContext();
3668 FunctionType *FuncTy = FunctionType::get(
3669 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3670 /* IsVarArg */ false);
3671 Function *WcFunc =
3673 "_omp_reduction_inter_warp_copy_func", &M);
3674 WcFunc->setCallingConv(Config.getRuntimeCC());
3675 WcFunc->setAttributes(FuncAttrs);
3676 WcFunc->addParamAttr(0, Attribute::NoUndef);
3677 WcFunc->addParamAttr(1, Attribute::NoUndef);
3678 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3679 Builder.SetInsertPoint(EntryBB);
3680 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3681
3682 // ReduceList: thread local Reduce list.
3683 // At the stage of the computation when this function is called, partially
3684 // aggregated values reside in the first lane of every active warp.
3685 Argument *ReduceListArg = WcFunc->getArg(0);
3686 // NumWarps: number of warps active in the parallel region. This could
3687 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3688 Argument *NumWarpsArg = WcFunc->getArg(1);
3689
3690 // This array is used as a medium to transfer, one reduce element at a time,
3691 // the data from the first lane of every warp to lanes in the first warp
3692 // in order to perform the final step of a reduction in a parallel region
3693 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3694 // for reduced latency, as well as to have a distinct copy for concurrently
3695 // executing target regions. The array is declared with common linkage so
3696 // as to be shared across compilation units.
3697 StringRef TransferMediumName =
3698 "__openmp_nvptx_data_transfer_temporary_storage";
3699 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3700 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3701 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3702 if (!TransferMedium) {
3703 TransferMedium = new GlobalVariable(
3704 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3705 UndefValue::get(ArrayTy), TransferMediumName,
3706 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3707 /*AddressSpace=*/3);
3708 }
3709
3710 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3711 Value *GPUThreadID = getGPUThreadID();
3712 // nvptx_lane_id = nvptx_id % warpsize
3713 Value *LaneID = getNVPTXLaneID();
3714 // nvptx_warp_id = nvptx_id / warpsize
3715 Value *WarpID = getNVPTXWarpID();
3716
3717 InsertPointTy AllocaIP =
3718 InsertPointTy(Builder.GetInsertBlock(),
3719 Builder.GetInsertBlock()->getFirstInsertionPt());
3720 Type *Arg0Type = ReduceListArg->getType();
3721 Type *Arg1Type = NumWarpsArg->getType();
3722 Builder.restoreIP(AllocaIP);
3723 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3724 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3725 AllocaInst *NumWarpsAlloca =
3726 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3727 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3728 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3729 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3730 NumWarpsAlloca, Builder.getPtrTy(0),
3731 NumWarpsAlloca->getName() + ".ascast");
3732 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3733 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3734 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3735 InsertPointTy CodeGenIP =
3736 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3737 Builder.restoreIP(CodeGenIP);
3738
3739 Value *ReduceList =
3740 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3741
3742 for (auto En : enumerate(ReductionInfos)) {
3743 //
3744 // Warp master copies reduce element to transfer medium in __shared__
3745 // memory.
3746 //
3747 const ReductionInfo &RI = En.value();
3748 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3749 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3750 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3751 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3752 Type *CType = Builder.getIntNTy(TySize * 8);
3753
3754 unsigned NumIters = RealTySize / TySize;
3755 if (NumIters == 0)
3756 continue;
3757 Value *Cnt = nullptr;
3758 Value *CntAddr = nullptr;
3759 BasicBlock *PrecondBB = nullptr;
3760 BasicBlock *ExitBB = nullptr;
3761 if (NumIters > 1) {
3762 CodeGenIP = Builder.saveIP();
3763 Builder.restoreIP(AllocaIP);
3764 CntAddr =
3765 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3766
3767 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3768 CntAddr->getName() + ".ascast");
3769 Builder.restoreIP(CodeGenIP);
3770 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3771 CntAddr,
3772 /*Volatile=*/false);
3773 PrecondBB = BasicBlock::Create(Ctx, "precond");
3774 ExitBB = BasicBlock::Create(Ctx, "exit");
3775 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3776 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3777 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3778 /*Volatile=*/false);
3779 Value *Cmp = Builder.CreateICmpULT(
3780 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3781 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3782 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3783 }
3784
3785 // kmpc_barrier.
3786 InsertPointOrErrorTy BarrierIP1 =
3788 omp::Directive::OMPD_unknown,
3789 /* ForceSimpleCall */ false,
3790 /* CheckCancelFlag */ true);
3791 if (!BarrierIP1)
3792 return BarrierIP1.takeError();
3793 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3794 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3795 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3796
3797 // if (lane_id == 0)
3798 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3799 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3800 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3801
3802 // Reduce element = LocalReduceList[i]
3803 auto *RedListArrayTy =
3804 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3805 Type *IndexTy = Builder.getIndexTy(
3806 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3807 Value *ElemPtrPtr =
3808 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3809 {ConstantInt::get(IndexTy, 0),
3810 ConstantInt::get(IndexTy, En.index())});
3811 // elemptr = ((CopyType*)(elemptrptr)) + I
3812 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3813
3814 if (IsByRefElem && RI.DataPtrPtrGen) {
3815 InsertPointOrErrorTy GenRes =
3816 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3817
3818 if (!GenRes)
3819 return GenRes.takeError();
3820
3821 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3822 }
3823
3824 if (NumIters > 1)
3825 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3826
3827 // Get pointer to location in transfer medium.
3828 // MediumPtr = &medium[warp_id]
3829 Value *MediumPtr = Builder.CreateInBoundsGEP(
3830 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3831 // elem = *elemptr
3832 //*MediumPtr = elem
3833 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3834 // Store the source element value to the dest element address.
3835 Builder.CreateStore(Elem, MediumPtr,
3836 /*IsVolatile*/ true);
3837 Builder.CreateBr(MergeBB);
3838
3839 // else
3840 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3841 Builder.CreateBr(MergeBB);
3842
3843 // endif
3844 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3845 InsertPointOrErrorTy BarrierIP2 =
3847 omp::Directive::OMPD_unknown,
3848 /* ForceSimpleCall */ false,
3849 /* CheckCancelFlag */ true);
3850 if (!BarrierIP2)
3851 return BarrierIP2.takeError();
3852
3853 // Warp 0 copies reduce element from transfer medium
3854 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3855 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3856 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3857
3858 Value *NumWarpsVal =
3859 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3860 // Up to 32 threads in warp 0 are active.
3861 Value *IsActiveThread =
3862 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3863 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3864
3865 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3866
3867 // SecMediumPtr = &medium[tid]
3868 // SrcMediumVal = *SrcMediumPtr
3869 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3870 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3871 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3872 Value *TargetElemPtrPtr =
3873 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3874 {ConstantInt::get(IndexTy, 0),
3875 ConstantInt::get(IndexTy, En.index())});
3876 Value *TargetElemPtrVal =
3877 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3878 Value *TargetElemPtr = TargetElemPtrVal;
3879
3880 if (IsByRefElem && RI.DataPtrPtrGen) {
3881 InsertPointOrErrorTy GenRes =
3882 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3883
3884 if (!GenRes)
3885 return GenRes.takeError();
3886
3887 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3888 }
3889
3890 if (NumIters > 1)
3891 TargetElemPtr =
3892 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3893
3894 // *TargetElemPtr = SrcMediumVal;
3895 Value *SrcMediumValue =
3896 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3897 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3898 Builder.CreateBr(W0MergeBB);
3899
3900 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3901 Builder.CreateBr(W0MergeBB);
3902
3903 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3904
3905 if (NumIters > 1) {
3906 Cnt = Builder.CreateNSWAdd(
3907 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3908 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3909
3910 auto *CurFn = Builder.GetInsertBlock()->getParent();
3911 emitBranch(PrecondBB);
3912 emitBlock(ExitBB, CurFn);
3913 }
3914 RealTySize %= TySize;
3915 }
3916 }
3917
3918 Builder.CreateRetVoid();
3919
3920 return WcFunc;
3921}
3922
3923Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3924 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3925 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3926 LLVMContext &Ctx = M.getContext();
3927 IRBuilder<>::InsertPointGuard IPG(Builder);
3928 FunctionType *FuncTy =
3929 FunctionType::get(Builder.getVoidTy(),
3930 {Builder.getPtrTy(), Builder.getInt16Ty(),
3931 Builder.getInt16Ty(), Builder.getInt16Ty()},
3932 /* IsVarArg */ false);
3933 Function *SarFunc =
3935 "_omp_reduction_shuffle_and_reduce_func", &M);
3936 SarFunc->setCallingConv(Config.getRuntimeCC());
3937 SarFunc->setAttributes(FuncAttrs);
3938 SarFunc->addParamAttr(0, Attribute::NoUndef);
3939 SarFunc->addParamAttr(1, Attribute::NoUndef);
3940 SarFunc->addParamAttr(2, Attribute::NoUndef);
3941 SarFunc->addParamAttr(3, Attribute::NoUndef);
3942 SarFunc->addParamAttr(1, Attribute::SExt);
3943 SarFunc->addParamAttr(2, Attribute::SExt);
3944 SarFunc->addParamAttr(3, Attribute::SExt);
3945 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3946 Builder.SetInsertPoint(EntryBB);
3947 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3948
3949 // Thread local Reduce list used to host the values of data to be reduced.
3950 Argument *ReduceListArg = SarFunc->getArg(0);
3951 // Current lane id; could be logical.
3952 Argument *LaneIDArg = SarFunc->getArg(1);
3953 // Offset of the remote source lane relative to the current lane.
3954 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
3955 // Algorithm version. This is expected to be known at compile time.
3956 Argument *AlgoVerArg = SarFunc->getArg(3);
3957
3958 Type *ReduceListArgType = ReduceListArg->getType();
3959 Type *LaneIDArgType = LaneIDArg->getType();
3960 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
3961 Value *ReduceListAlloca = Builder.CreateAlloca(
3962 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
3963 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3964 LaneIDArg->getName() + ".addr");
3965 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
3966 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
3967 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3968 AlgoVerArg->getName() + ".addr");
3969 ArrayType *RedListArrayTy =
3970 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3971
3972 // Create a local thread-private variable to host the Reduce list
3973 // from a remote lane.
3974 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
3975 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
3976
3977 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3978 ReduceListAlloca, ReduceListArgType,
3979 ReduceListAlloca->getName() + ".ascast");
3980 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3981 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
3982 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3983 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
3984 RemoteLaneOffsetAlloca->getName() + ".ascast");
3985 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3986 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
3987 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3988 RemoteReductionListAlloca, Builder.getPtrTy(),
3989 RemoteReductionListAlloca->getName() + ".ascast");
3990
3991 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3992 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
3993 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
3994 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
3995
3996 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
3997 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
3998 Value *RemoteLaneOffset =
3999 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4000 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4001
4002 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4003
4004 // This loop iterates through the list of reduce elements and copies,
4005 // element by element, from a remote lane in the warp to RemoteReduceList,
4006 // hosted on the thread's stack.
4007 Error EmitRedLsCpRes = emitReductionListCopy(
4008 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4009 ReduceList, RemoteListAddrCast, IsByRef,
4010 {RemoteLaneOffset, nullptr, nullptr});
4011
4012 if (EmitRedLsCpRes)
4013 return EmitRedLsCpRes;
4014
4015 // The actions to be performed on the Remote Reduce list is dependent
4016 // on the algorithm version.
4017 //
4018 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4019 // LaneId % 2 == 0 && Offset > 0):
4020 // do the reduction value aggregation
4021 //
4022 // The thread local variable Reduce list is mutated in place to host the
4023 // reduced data, which is the aggregated value produced from local and
4024 // remote lanes.
4025 //
4026 // Note that AlgoVer is expected to be a constant integer known at compile
4027 // time.
4028 // When AlgoVer==0, the first conjunction evaluates to true, making
4029 // the entire predicate true during compile time.
4030 // When AlgoVer==1, the second conjunction has only the second part to be
4031 // evaluated during runtime. Other conjunctions evaluates to false
4032 // during compile time.
4033 // When AlgoVer==2, the third conjunction has only the second part to be
4034 // evaluated during runtime. Other conjunctions evaluates to false
4035 // during compile time.
4036 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4037 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4038 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4039 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4040 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4041 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4042 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4043 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4044 Value *RemoteOffsetComp =
4045 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4046 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4047 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4048 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4049
4050 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4051 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4052 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4053
4054 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4055 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4056 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4057 ReduceList, Builder.getPtrTy());
4058 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4059 RemoteListAddrCast, Builder.getPtrTy());
4060 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4061 ->addFnAttr(Attribute::NoUnwind);
4062 Builder.CreateBr(MergeBB);
4063
4064 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4065 Builder.CreateBr(MergeBB);
4066
4067 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4068
4069 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4070 // Reduce list.
4071 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4072 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4073 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4074
4075 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4076 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4077 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4078 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4079
4080 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4081
4082 EmitRedLsCpRes = emitReductionListCopy(
4083 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4084 RemoteListAddrCast, ReduceList, IsByRef);
4085
4086 if (EmitRedLsCpRes)
4087 return EmitRedLsCpRes;
4088
4089 Builder.CreateBr(CpyMergeBB);
4090
4091 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4092 Builder.CreateBr(CpyMergeBB);
4093
4094 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4095
4096 Builder.CreateRetVoid();
4097
4098 return SarFunc;
4099}
4100
4102OpenMPIRBuilder::generateReductionDescriptor(
4103 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4104 Type *DescriptorType,
4105 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4106 DataPtrPtrGen) {
4107
4108 // Copy the source descriptor to preserve all metadata (rank, extents,
4109 // strides, etc.)
4110 Value *DescriptorSize =
4111 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4112 Builder.CreateMemCpy(
4113 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4114 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4115 DescriptorSize);
4116
4117 // Update the base pointer field to point to the local shuffled data
4118 Value *DataPtrField;
4119 InsertPointOrErrorTy GenResult =
4120 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4121
4122 if (!GenResult)
4123 return GenResult.takeError();
4124
4125 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4126 DataPtr, Builder.getPtrTy(), ".ascast"),
4127 DataPtrField);
4128
4129 return Builder.saveIP();
4130}
4131
4132Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4133 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4134 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4135 InsertPointTy OldIP = Builder.saveIP();
4136 Builder.restoreIP(AllocaIP);
4137
4138 AllocaInst *DescriptorAlloca =
4139 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4140 DescriptorAlloca->setAlignment(
4141 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4142 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4143 DescriptorAlloca, DescriptorPtrTy,
4144 DescriptorAlloca->getName() + ".ascast");
4145
4146 Builder.restoreIP(OldIP);
4147
4148 InsertPointOrErrorTy GenResult =
4149 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4150 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4151 if (!GenResult)
4152 return GenResult.takeError();
4153
4154 return DescriptorAddr;
4155}
4156
4157Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4158 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4159 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4160 IRBuilder<>::InsertPointGuard IPG(Builder);
4161 LLVMContext &Ctx = M.getContext();
4162 FunctionType *FuncTy = FunctionType::get(
4163 Builder.getVoidTy(),
4164 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4165 /* IsVarArg */ false);
4166 Function *LtGCFunc =
4168 "_omp_reduction_list_to_global_copy_func", &M);
4169 LtGCFunc->setAttributes(FuncAttrs);
4170 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4171 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4172 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4173
4174 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4175 Builder.SetInsertPoint(EntryBlock);
4176 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4177
4178 // Buffer: global reduction buffer.
4179 Argument *BufferArg = LtGCFunc->getArg(0);
4180 // Idx: index of the buffer.
4181 Argument *IdxArg = LtGCFunc->getArg(1);
4182 // ReduceList: thread local Reduce list.
4183 Argument *ReduceListArg = LtGCFunc->getArg(2);
4184
4185 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4186 BufferArg->getName() + ".addr");
4187 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4188 IdxArg->getName() + ".addr");
4189 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4190 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4191 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4192 BufferArgAlloca, Builder.getPtrTy(),
4193 BufferArgAlloca->getName() + ".ascast");
4194 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4195 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4196 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4197 ReduceListArgAlloca, Builder.getPtrTy(),
4198 ReduceListArgAlloca->getName() + ".ascast");
4199
4200 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4201 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4202 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4203
4204 Value *LocalReduceList =
4205 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4206 Value *BufferArgVal =
4207 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4208 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4209 Type *IndexTy = Builder.getIndexTy(
4210 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4211 for (auto En : enumerate(ReductionInfos)) {
4212 const ReductionInfo &RI = En.value();
4213 auto *RedListArrayTy =
4214 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4215 // Reduce element = LocalReduceList[i]
4216 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4217 RedListArrayTy, LocalReduceList,
4218 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4219 // elemptr = ((CopyType*)(elemptrptr)) + I
4220 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4221
4222 // Global = Buffer.VD[Idx];
4223 Value *BufferVD =
4224 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4225 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4226 ReductionsBufferTy, BufferVD, 0, En.index());
4227
4228 switch (RI.EvaluationKind) {
4229 case EvalKind::Scalar: {
4230 Value *TargetElement;
4231
4232 if (IsByRef.empty() || !IsByRef[En.index()]) {
4233 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4234 } else {
4235 if (RI.DataPtrPtrGen) {
4236 InsertPointOrErrorTy GenResult =
4237 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4238
4239 if (!GenResult)
4240 return GenResult.takeError();
4241
4242 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4243 }
4244 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4245 }
4246
4247 Builder.CreateStore(TargetElement, GlobVal);
4248 break;
4249 }
4250 case EvalKind::Complex: {
4251 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4252 RI.ElementType, ElemPtr, 0, 0, ".realp");
4253 Value *SrcReal = Builder.CreateLoad(
4254 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4255 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4256 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4257 Value *SrcImg = Builder.CreateLoad(
4258 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4259
4260 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4261 RI.ElementType, GlobVal, 0, 0, ".realp");
4262 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4263 RI.ElementType, GlobVal, 0, 1, ".imagp");
4264 Builder.CreateStore(SrcReal, DestRealPtr);
4265 Builder.CreateStore(SrcImg, DestImgPtr);
4266 break;
4267 }
4268 case EvalKind::Aggregate: {
4269 Value *SizeVal =
4270 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4271 Builder.CreateMemCpy(
4272 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4273 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4274 break;
4275 }
4276 }
4277 }
4278
4279 Builder.CreateRetVoid();
4280 return LtGCFunc;
4281}
4282
4283Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4284 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4285 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4286 IRBuilder<>::InsertPointGuard IPG(Builder);
4287 LLVMContext &Ctx = M.getContext();
4288 FunctionType *FuncTy = FunctionType::get(
4289 Builder.getVoidTy(),
4290 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4291 /* IsVarArg */ false);
4292 Function *LtGRFunc =
4294 "_omp_reduction_list_to_global_reduce_func", &M);
4295 LtGRFunc->setAttributes(FuncAttrs);
4296 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4297 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4298 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4299
4300 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4301 Builder.SetInsertPoint(EntryBlock);
4302 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4303
4304 // Buffer: global reduction buffer.
4305 Argument *BufferArg = LtGRFunc->getArg(0);
4306 // Idx: index of the buffer.
4307 Argument *IdxArg = LtGRFunc->getArg(1);
4308 // ReduceList: thread local Reduce list.
4309 Argument *ReduceListArg = LtGRFunc->getArg(2);
4310
4311 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4312 BufferArg->getName() + ".addr");
4313 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4314 IdxArg->getName() + ".addr");
4315 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4316 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4317 auto *RedListArrayTy =
4318 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4319
4320 // 1. Build a list of reduction variables.
4321 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4322 Value *LocalReduceList =
4323 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4324
4325 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4326
4327 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4328 BufferArgAlloca, Builder.getPtrTy(),
4329 BufferArgAlloca->getName() + ".ascast");
4330 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4331 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4332 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4333 ReduceListArgAlloca, Builder.getPtrTy(),
4334 ReduceListArgAlloca->getName() + ".ascast");
4335 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4336 LocalReduceList, Builder.getPtrTy(),
4337 LocalReduceList->getName() + ".ascast");
4338
4339 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4340 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4341 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4342
4343 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4344 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4345 Type *IndexTy = Builder.getIndexTy(
4346 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4347 for (auto En : enumerate(ReductionInfos)) {
4348 const ReductionInfo &RI = En.value();
4349
4350 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4351 RedListArrayTy, LocalReduceListAddrCast,
4352 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4353 Value *BufferVD =
4354 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4355 // Global = Buffer.VD[Idx];
4356 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4357 ReductionsBufferTy, BufferVD, 0, En.index());
4358
4359 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4360 // Get source descriptor from the reduce list argument
4361 Value *ReduceList =
4362 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4363 Value *SrcElementPtrPtr =
4364 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4365 {ConstantInt::get(IndexTy, 0),
4366 ConstantInt::get(IndexTy, En.index())});
4367 Value *SrcDescriptorAddr =
4368 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4369
4370 // Copy descriptor from source and update base_ptr to global buffer data
4371 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4372 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4373 if (!ByRefAlloc)
4374 return ByRefAlloc.takeError();
4375
4376 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4377 } else {
4378 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4379 }
4380 }
4381
4382 // Call reduce_function(GlobalReduceList, ReduceList)
4383 Value *ReduceList =
4384 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4385 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4386 ->addFnAttr(Attribute::NoUnwind);
4387 Builder.CreateRetVoid();
4388 return LtGRFunc;
4389}
4390
4391Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4392 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4393 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4394 IRBuilder<>::InsertPointGuard IPG(Builder);
4395 LLVMContext &Ctx = M.getContext();
4396 FunctionType *FuncTy = FunctionType::get(
4397 Builder.getVoidTy(),
4398 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4399 /* IsVarArg */ false);
4400 Function *GtLCFunc =
4402 "_omp_reduction_global_to_list_copy_func", &M);
4403 GtLCFunc->setAttributes(FuncAttrs);
4404 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4405 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4406 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4407
4408 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4409 Builder.SetInsertPoint(EntryBlock);
4410 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4411
4412 // Buffer: global reduction buffer.
4413 Argument *BufferArg = GtLCFunc->getArg(0);
4414 // Idx: index of the buffer.
4415 Argument *IdxArg = GtLCFunc->getArg(1);
4416 // ReduceList: thread local Reduce list.
4417 Argument *ReduceListArg = GtLCFunc->getArg(2);
4418
4419 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4420 BufferArg->getName() + ".addr");
4421 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4422 IdxArg->getName() + ".addr");
4423 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4424 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4425 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4426 BufferArgAlloca, Builder.getPtrTy(),
4427 BufferArgAlloca->getName() + ".ascast");
4428 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4429 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4430 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4431 ReduceListArgAlloca, Builder.getPtrTy(),
4432 ReduceListArgAlloca->getName() + ".ascast");
4433 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4434 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4435 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4436
4437 Value *LocalReduceList =
4438 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4439 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4440 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4441 Type *IndexTy = Builder.getIndexTy(
4442 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4443 for (auto En : enumerate(ReductionInfos)) {
4444 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4445 auto *RedListArrayTy =
4446 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4447 // Reduce element = LocalReduceList[i]
4448 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4449 RedListArrayTy, LocalReduceList,
4450 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4451 // elemptr = ((CopyType*)(elemptrptr)) + I
4452 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4453 // Global = Buffer.VD[Idx];
4454 Value *BufferVD =
4455 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4456 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4457 ReductionsBufferTy, BufferVD, 0, En.index());
4458
4459 switch (RI.EvaluationKind) {
4460 case EvalKind::Scalar: {
4461 Type *ElemType = RI.ElementType;
4462
4463 if (!IsByRef.empty() && IsByRef[En.index()]) {
4464 ElemType = RI.ByRefElementType;
4465 if (RI.DataPtrPtrGen) {
4466 InsertPointOrErrorTy GenResult =
4467 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4468
4469 if (!GenResult)
4470 return GenResult.takeError();
4471
4472 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4473 }
4474 }
4475
4476 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4477 Builder.CreateStore(TargetElement, ElemPtr);
4478 break;
4479 }
4480 case EvalKind::Complex: {
4481 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4482 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4483 Value *SrcReal = Builder.CreateLoad(
4484 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4485 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4486 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4487 Value *SrcImg = Builder.CreateLoad(
4488 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4489
4490 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4491 RI.ElementType, ElemPtr, 0, 0, ".realp");
4492 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4493 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4494 Builder.CreateStore(SrcReal, DestRealPtr);
4495 Builder.CreateStore(SrcImg, DestImgPtr);
4496 break;
4497 }
4498 case EvalKind::Aggregate: {
4499 Value *SizeVal =
4500 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4501 Builder.CreateMemCpy(
4502 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4503 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4504 SizeVal, false);
4505 break;
4506 }
4507 }
4508 }
4509
4510 Builder.CreateRetVoid();
4511 return GtLCFunc;
4512}
4513
4514Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4515 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4516 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4517 IRBuilder<>::InsertPointGuard IPG(Builder);
4518 LLVMContext &Ctx = M.getContext();
4519 auto *FuncTy = FunctionType::get(
4520 Builder.getVoidTy(),
4521 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4522 /* IsVarArg */ false);
4523 Function *GtLRFunc =
4525 "_omp_reduction_global_to_list_reduce_func", &M);
4526 GtLRFunc->setAttributes(FuncAttrs);
4527 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4528 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4529 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4530
4531 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4532 Builder.SetInsertPoint(EntryBlock);
4533 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4534
4535 // Buffer: global reduction buffer.
4536 Argument *BufferArg = GtLRFunc->getArg(0);
4537 // Idx: index of the buffer.
4538 Argument *IdxArg = GtLRFunc->getArg(1);
4539 // ReduceList: thread local Reduce list.
4540 Argument *ReduceListArg = GtLRFunc->getArg(2);
4541
4542 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4543 BufferArg->getName() + ".addr");
4544 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4545 IdxArg->getName() + ".addr");
4546 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4547 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4548 ArrayType *RedListArrayTy =
4549 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4550
4551 // 1. Build a list of reduction variables.
4552 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4553 Value *LocalReduceList =
4554 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4555
4556 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4557
4558 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4559 BufferArgAlloca, Builder.getPtrTy(),
4560 BufferArgAlloca->getName() + ".ascast");
4561 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4562 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4563 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4564 ReduceListArgAlloca, Builder.getPtrTy(),
4565 ReduceListArgAlloca->getName() + ".ascast");
4566 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4567 LocalReduceList, Builder.getPtrTy(),
4568 LocalReduceList->getName() + ".ascast");
4569
4570 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4571 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4572 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4573
4574 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4575 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4576 Type *IndexTy = Builder.getIndexTy(
4577 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4578 for (auto En : enumerate(ReductionInfos)) {
4579 const ReductionInfo &RI = En.value();
4580
4581 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4582 RedListArrayTy, ReductionList,
4583 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4584 // Global = Buffer.VD[Idx];
4585 Value *BufferVD =
4586 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4587 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4588 ReductionsBufferTy, BufferVD, 0, En.index());
4589
4590 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4591 // Get source descriptor from the reduce list
4592 Value *ReduceListVal =
4593 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4594 Value *SrcElementPtrPtr =
4595 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4596 {ConstantInt::get(IndexTy, 0),
4597 ConstantInt::get(IndexTy, En.index())});
4598 Value *SrcDescriptorAddr =
4599 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4600
4601 // Copy descriptor from source and update base_ptr to global buffer data
4602 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4603 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4604 if (!ByRefAlloc)
4605 return ByRefAlloc.takeError();
4606
4607 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4608 } else {
4609 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4610 }
4611 }
4612
4613 // Call reduce_function(ReduceList, GlobalReduceList)
4614 Value *ReduceList =
4615 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4616 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4617 ->addFnAttr(Attribute::NoUnwind);
4618 Builder.CreateRetVoid();
4619 return GtLRFunc;
4620}
4621
4622std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4623 std::string Suffix =
4624 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4625 return (Name + Suffix).str();
4626}
4627
4628Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4629 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4631 AttributeList FuncAttrs) {
4632 IRBuilder<>::InsertPointGuard IPG(Builder);
4633 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4634 {Builder.getPtrTy(), Builder.getPtrTy()},
4635 /* IsVarArg */ false);
4636 std::string Name = getReductionFuncName(ReducerName);
4637 Function *ReductionFunc =
4639 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4640 ReductionFunc->setAttributes(FuncAttrs);
4641 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4642 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4643 BasicBlock *EntryBB =
4644 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4645 Builder.SetInsertPoint(EntryBB);
4646 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4647
4648 // Need to alloca memory here and deal with the pointers before getting
4649 // LHS/RHS pointers out
4650 Value *LHSArrayPtr = nullptr;
4651 Value *RHSArrayPtr = nullptr;
4652 Argument *Arg0 = ReductionFunc->getArg(0);
4653 Argument *Arg1 = ReductionFunc->getArg(1);
4654 Type *Arg0Type = Arg0->getType();
4655 Type *Arg1Type = Arg1->getType();
4656
4657 Value *LHSAlloca =
4658 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4659 Value *RHSAlloca =
4660 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4661 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4662 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4663 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4664 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4665 Builder.CreateStore(Arg0, LHSAddrCast);
4666 Builder.CreateStore(Arg1, RHSAddrCast);
4667 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4668 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4669
4670 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4671 Type *IndexTy = Builder.getIndexTy(
4672 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4673 SmallVector<Value *> LHSPtrs, RHSPtrs;
4674 for (auto En : enumerate(ReductionInfos)) {
4675 const ReductionInfo &RI = En.value();
4676 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4677 RedArrayTy, RHSArrayPtr,
4678 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4679 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4680 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4681 RHSI8Ptr, RI.PrivateVariable->getType(),
4682 RHSI8Ptr->getName() + ".ascast");
4683
4684 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4685 RedArrayTy, LHSArrayPtr,
4686 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4687 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4688 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4689 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4690
4692 LHSPtrs.emplace_back(LHSPtr);
4693 RHSPtrs.emplace_back(RHSPtr);
4694 } else {
4695 Value *LHS = LHSPtr;
4696 Value *RHS = RHSPtr;
4697
4698 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4699 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4700 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4701 }
4702
4703 Value *Reduced;
4704 InsertPointOrErrorTy AfterIP =
4705 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4706 if (!AfterIP)
4707 return AfterIP.takeError();
4708 if (!Builder.GetInsertBlock())
4709 return ReductionFunc;
4710
4711 Builder.restoreIP(*AfterIP);
4712
4713 if (!IsByRef.empty() && !IsByRef[En.index()])
4714 Builder.CreateStore(Reduced, LHSPtr);
4715 }
4716 }
4717
4719 for (auto En : enumerate(ReductionInfos)) {
4720 unsigned Index = En.index();
4721 const ReductionInfo &RI = En.value();
4722 Value *LHSFixupPtr, *RHSFixupPtr;
4723 Builder.restoreIP(RI.ReductionGenClang(
4724 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4725
4726 // Fix the CallBack code genereated to use the correct Values for the LHS
4727 // and RHS
4728 LHSFixupPtr->replaceUsesWithIf(
4729 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4730 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4731 ReductionFunc;
4732 });
4733 RHSFixupPtr->replaceUsesWithIf(
4734 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4735 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4736 ReductionFunc;
4737 });
4738 }
4739
4740 Builder.CreateRetVoid();
4741 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4742 // to the entry block (this is dones for higher opt levels by later passes in
4743 // the pipeline). This has caused issues because non-entry `alloca`s force the
4744 // function to use dynamic stack allocations and we might run out of scratch
4745 // memory.
4746 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4747
4748 return ReductionFunc;
4749}
4750
4751static void
4753 bool IsGPU) {
4754 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4755 (void)RI;
4756 assert(RI.Variable && "expected non-null variable");
4757 assert(RI.PrivateVariable && "expected non-null private variable");
4758 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4759 "expected non-null reduction generator callback");
4760 if (!IsGPU) {
4761 assert(
4762 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4763 "expected variables and their private equivalents to have the same "
4764 "type");
4765 }
4766 assert(RI.Variable->getType()->isPointerTy() &&
4767 "expected variables to be pointers");
4768 }
4769}
4770
4771// The atomic cross-team reduction fast path applies when every reduction in the
4772// set can be represented by an atomicrmw. Clang only populates it for scalar
4773// reductions with a supported atomic operator.
4776 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4777 return static_cast<bool>(RI.AtomicReductionGen);
4778 });
4779}
4780
4782 const LocationDescription &Loc, InsertPointTy AllocaIP,
4783 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4784 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4785 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4786 Value *SrcLocInfo) {
4787 if (!updateToLocation(Loc))
4788 return InsertPointTy();
4789 Builder.restoreIP(CodeGenIP);
4790 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4791 LLVMContext &Ctx = M.getContext();
4792
4793 // Source location for the ident struct
4794 if (!SrcLocInfo) {
4795 uint32_t SrcLocStrSize;
4796 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4797 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4798 }
4799
4800 if (ReductionInfos.size() == 0)
4801 return Builder.saveIP();
4802
4803 BasicBlock *ContinuationBlock = nullptr;
4805 // Copied code from createReductions
4806 BasicBlock *InsertBlock = Loc.IP.getBlock();
4807 ContinuationBlock =
4808 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
4809 InsertBlock->getTerminator()->eraseFromParent();
4810 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
4811 }
4812
4813 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4814 AttributeList FuncAttrs;
4815 AttrBuilder AttrBldr(Ctx);
4816 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4817 AttrBldr.addAttribute(Attr);
4818 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4819 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4820
4821 CodeGenIP = Builder.saveIP();
4822 Expected<Function *> ReductionResult = createReductionFunction(
4823 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4824 ReductionGenCBKind, FuncAttrs);
4825 if (!ReductionResult)
4826 return ReductionResult.takeError();
4827 Function *ReductionFunc = *ReductionResult;
4828 Builder.restoreIP(CodeGenIP);
4829
4830 // Set the grid value in the config needed for lowering later on
4831 if (GridValue.has_value())
4832 Config.setGridValue(GridValue.value());
4833 else
4834 Config.setGridValue(getGridValue(T, ReductionFunc));
4835
4836 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4837 // RedList, shuffle_reduce_func, interwarp_copy_func);
4838 // or
4839 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4840 Value *Res;
4841
4842 // 1. Build a list of reduction variables.
4843 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4844 auto Size = ReductionInfos.size();
4845 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4846 Type *FuncPtrTy =
4847 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4848 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4849 CodeGenIP = Builder.saveIP();
4850 Builder.restoreIP(AllocaIP);
4851 Value *ReductionListAlloca =
4852 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4853 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4854 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4855 Builder.restoreIP(CodeGenIP);
4856 Type *IndexTy = Builder.getIndexTy(
4857 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4858 for (auto En : enumerate(ReductionInfos)) {
4859 const ReductionInfo &RI = En.value();
4860 Value *ElemPtr = Builder.CreateInBoundsGEP(
4861 RedArrayTy, ReductionList,
4862 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4863
4864 Value *PrivateVar = RI.PrivateVariable;
4865 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4866 if (IsByRefElem)
4867 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4868
4869 Value *CastElem =
4870 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4871 Builder.CreateStore(CastElem, ElemPtr);
4872 }
4873 CodeGenIP = Builder.saveIP();
4874 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4875 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4876
4877 if (!SarFunc)
4878 return SarFunc.takeError();
4879
4880 Expected<Function *> CopyResult =
4881 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4882 if (!CopyResult)
4883 return CopyResult.takeError();
4884 Function *WcFunc = *CopyResult;
4885 Builder.restoreIP(CodeGenIP);
4886
4887 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4888
4889 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4890 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4891 // not currently use it. It is computed here conservatively as max(element
4892 // sizes) * N rather than the exact sum, which over-calculates the size for
4893 // mixed reduction types but is harmless given the argument is unused.
4894 // TODO: Consider dropping this computation if the runtime API is ever revised
4895 // to remove the unused parameter.
4896 unsigned MaxDataSize = 0;
4897 SmallVector<Type *> ReductionTypeArgs;
4898 for (auto En : enumerate(ReductionInfos)) {
4899 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4900 // the actual data size stored in the global reduction buffer, consistent
4901 // with the ReductionsBufferTy struct used for GEP offsets below.
4902 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4903 ? En.value().ByRefElementType
4904 : En.value().ElementType;
4905 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4906 if (Size > MaxDataSize)
4907 MaxDataSize = Size;
4908 ReductionTypeArgs.emplace_back(RedTypeArg);
4909 }
4910 Value *ReductionDataSize =
4911 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4912
4913 // Helper function to copy thread-local data back to the original reduction
4914 // list.
4915 Function *CopyScratchToListFunc = nullptr;
4916 // Thread-local storage for the reduction variables.
4917 Value *ScratchForCopyBack = nullptr;
4918 // RL pointer to which the final value from the per-thread scratch should be
4919 // copied back. (Basically RL, appropriately casted if necessary.)
4920 Value *RLForCopyBack = RL;
4921
4922 bool IsAtomicReduction =
4923 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4924
4925 if (!IsTeamsReduction) {
4926 Value *SarFuncCast =
4927 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4928 Value *WcFuncCast =
4929 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4930 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4931 WcFuncCast};
4933 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4934 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4935 } else if (IsAtomicReduction) {
4936 // Atomic cross-team reduction fast path: determine the team's main thread
4937 // that is later to fold its value atomically into the mapped variable.
4938 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4939 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4940 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4941 } else {
4942 CodeGenIP = Builder.saveIP();
4943 StructType *ReductionsBufferTy = StructType::create(
4944 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4945
4946 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4947 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4948 if (!LtGCFunc)
4949 return LtGCFunc.takeError();
4950
4951 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
4952 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4953 if (!GtLCFunc)
4954 return GtLCFunc.takeError();
4955
4956 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
4957 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
4958 if (!GtLRFunc)
4959 return GtLRFunc.takeError();
4960
4961 Builder.restoreIP(CodeGenIP);
4962
4963 // The runtime's cross-team final aggregate uses the storage pointed at by
4964 // its reduce-list argument as per-thread scratch. When the surrounding
4965 // kernel is already in SPMD execution mode, clang emitted each reduction
4966 // private as a per-thread `alloca addrspace(5)`, so the original red_list
4967 // (RL) is already per-thread and nothing else is needed.
4968 //
4969 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
4970 // Generic-mode globalization put the reduction private into team-shared
4971 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
4972 // point all threads of the last team would race on the shared LDS slot.
4973 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
4974 // value in, and hand the per-thread RL to the runtime instead. The writer
4975 // thread copies the final value from that per-thread scratch back to RL
4976 // before running the existing combine path below.
4977
4978 // Thread-local RL (might need localization below before being passed to the
4979 // runtime).
4980 Value *RuntimeRL = RL;
4981
4982 if (!IsSPMD) {
4983 CodeGenIP = Builder.saveIP();
4984 Builder.restoreIP(AllocaIP);
4985 // Allocate thread-local buffer for the reduction variables.
4986 Value *PerThreadScratchAlloca = Builder.CreateAlloca(
4987 ReductionsBufferTy, /*ArraySize=*/nullptr, ".omp.reduction.scratch");
4988 Value *PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
4989 PerThreadScratchAlloca, PtrTy,
4990 PerThreadScratchAlloca->getName() + ".ascast");
4991 // Allocate thread-local buffer for the pointers to the reduction
4992 // variables.
4993 Value *PerThreadRedListAlloca =
4994 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
4995 ".omp.reduction.per_thread_red_list");
4996 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
4997 PerThreadRedListAlloca, PtrTy,
4998 PerThreadRedListAlloca->getName() + ".ascast");
4999 Builder.restoreIP(CodeGenIP);
5000
5001 // Iterate over the reduction variables and copy the team-local value to
5002 // the thread-local buffer.
5003 for (auto En : enumerate(ReductionInfos)) {
5004 const ReductionInfo &RI = En.value();
5005 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5006
5007 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5008 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5009 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5010 0, En.index());
5011
5012 Value *RuntimeListEntry = FieldPtr;
5013 if (IsByRefElem && RI.DataPtrPtrGen) {
5014 Value *SrcDescriptor =
5015 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5016 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5017 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5018 if (!Descriptor)
5019 return Descriptor.takeError();
5020 RuntimeListEntry = *Descriptor;
5021 }
5022 Builder.CreateStore(RuntimeListEntry, Slot);
5023 }
5024 // The copy helpers were emitted with default-AS (AS 0) pointer params
5025 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5026 // but PerThreadScratch and RL live in the target's default AS, which
5027 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5028 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5029 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5030 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5031 PerThreadScratch, CopyArg0Ty);
5032 RLForCopyBack =
5033 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5034 // Use index 0 because there is no array of target values to index into,
5035 // there is only one thread-local memory slot.
5036 // restoreIP above left a stale/empty debug location; this inlinable call
5037 // to a debug-info-bearing helper needs one or the verifier rejects the
5038 // module ("!dbg attachment points at wrong subprogram") after inlining.
5039 Builder.SetCurrentDebugLocation(Loc.DL);
5040 Builder.CreateCall(
5041 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5042 CopyScratchToListFunc = *GtLCFunc;
5043 }
5044
5045 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5046 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5047
5048 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5049 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5050 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5051 }
5052
5053 // 5. Build if (res == 1)
5054 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5055 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5056 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5057 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5058
5059 // 6. Build then branch: where we have reduced values in the master
5060 // thread in each team.
5061 // __kmpc_end_reduce{_nowait}(<gtid>);
5062 // break;
5063 emitBlock(ThenBB, CurFunc);
5064
5065 // Copy the writer thread's per-thread scratch result back into the original
5066 // red-list storage before the existing combine path reads RI.PrivateVariable.
5067 // Set a debug location: this inlinable call to a debug-info-bearing helper
5068 // needs one or the verifier rejects the module after inlining.
5069 if (ScratchForCopyBack) {
5070 Builder.SetCurrentDebugLocation(Loc.DL);
5071 Builder.CreateCall(
5072 CopyScratchToListFunc,
5073 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5074 }
5075
5076 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5077 for (auto En : enumerate(ReductionInfos)) {
5078 const ReductionInfo &RI = En.value();
5079
5080 // Atomic cross-team fast path: each team's main thread folds its
5081 // team-reduced value directly into the mapped reduction variable with a
5082 // single atomicrmw.
5083 if (IsAtomicReduction) {
5085 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5086 if (!AfterIP)
5087 return AfterIP.takeError();
5088 Builder.restoreIP(*AfterIP);
5089 continue;
5090 }
5091
5093 Value *RedValue = RI.Variable;
5094
5095 Value *RHS =
5096 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5097
5099 Value *LHSPtr, *RHSPtr;
5100 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5101 &LHSPtr, &RHSPtr, CurFunc));
5102
5103 // Fix the CallBack code genereated to use the correct Values for the LHS
5104 // and RHS. Cast to match types before replacing (necessary to handle
5105 // different address spaces).
5106 if (LHSPtr->getType() != RedValue->getType())
5107 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5108 RedValue, LHSPtr->getType());
5109 if (RHSPtr->getType() != RHS->getType())
5110 RHS =
5111 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5112
5113 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5114 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5115 ReductionFunc;
5116 });
5117 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5118 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5119 ReductionFunc;
5120 });
5121 } else {
5122 if (IsByRef.empty() || !IsByRef[En.index()]) {
5123 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5124 "red.value." + Twine(En.index()));
5125 }
5126 Value *PrivateRedValue = Builder.CreateLoad(
5127 ValueType, RHS, "red.private.value" + Twine(En.index()));
5128 Value *Reduced;
5129 InsertPointOrErrorTy AfterIP =
5130 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5131 if (!AfterIP)
5132 return AfterIP.takeError();
5133 Builder.restoreIP(*AfterIP);
5134
5135 if (!IsByRef.empty() && !IsByRef[En.index()])
5136 Builder.CreateStore(Reduced, RI.Variable);
5137 }
5138 }
5139 emitBlock(ExitBB, CurFunc);
5140 if (ContinuationBlock) {
5141 Builder.CreateBr(ContinuationBlock);
5142 Builder.SetInsertPoint(ContinuationBlock);
5143 }
5144 Config.setEmitLLVMUsed();
5145
5146 return Builder.saveIP();
5147}
5148
5150 Type *VoidTy = Type::getVoidTy(M.getContext());
5151 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5152 auto *FuncTy =
5153 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5155 ".omp.reduction.func", &M);
5156}
5157
5159 Function *ReductionFunc,
5161 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5162 IRBuilder<>::InsertPointGuard IPG(Builder);
5163 Module *Module = ReductionFunc->getParent();
5164 BasicBlock *ReductionFuncBlock =
5165 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5166 Builder.SetInsertPoint(ReductionFuncBlock);
5167 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5168 Value *LHSArrayPtr = nullptr;
5169 Value *RHSArrayPtr = nullptr;
5170 if (IsGPU) {
5171 // Need to alloca memory here and deal with the pointers before getting
5172 // LHS/RHS pointers out
5173 //
5174 Argument *Arg0 = ReductionFunc->getArg(0);
5175 Argument *Arg1 = ReductionFunc->getArg(1);
5176 Type *Arg0Type = Arg0->getType();
5177 Type *Arg1Type = Arg1->getType();
5178
5179 Value *LHSAlloca =
5180 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5181 Value *RHSAlloca =
5182 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5183 Value *LHSAddrCast =
5184 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5185 Value *RHSAddrCast =
5186 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5187 Builder.CreateStore(Arg0, LHSAddrCast);
5188 Builder.CreateStore(Arg1, RHSAddrCast);
5189 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5190 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5191 } else {
5192 LHSArrayPtr = ReductionFunc->getArg(0);
5193 RHSArrayPtr = ReductionFunc->getArg(1);
5194 }
5195
5196 unsigned NumReductions = ReductionInfos.size();
5197 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5198
5199 for (auto En : enumerate(ReductionInfos)) {
5200 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5201 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5202 RedArrayTy, LHSArrayPtr, 0, En.index());
5203 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5204 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5205 LHSI8Ptr, RI.Variable->getType());
5206 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5207 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5208 RedArrayTy, RHSArrayPtr, 0, En.index());
5209 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5210 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5211 RHSI8Ptr, RI.PrivateVariable->getType());
5212 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5213 Value *Reduced;
5215 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5216 if (!AfterIP)
5217 return AfterIP.takeError();
5218
5219 Builder.restoreIP(*AfterIP);
5220 // TODO: Consider flagging an error.
5221 if (!Builder.GetInsertBlock())
5222 return Error::success();
5223
5224 // store is inside of the reduction region when using by-ref
5225 if (!IsByRef[En.index()])
5226 Builder.CreateStore(Reduced, LHSPtr);
5227 }
5228 Builder.CreateRetVoid();
5229 return Error::success();
5230}
5231
5233 const LocationDescription &Loc, InsertPointTy AllocaIP,
5234 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5235 bool IsNoWait, bool IsTeamsReduction) {
5236 assert(ReductionInfos.size() == IsByRef.size());
5237 if (Config.isGPU())
5238 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5239 IsByRef, IsNoWait, IsTeamsReduction);
5240
5241 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5242
5243 if (!updateToLocation(Loc))
5244 return InsertPointTy();
5245
5246 if (ReductionInfos.size() == 0)
5247 return Builder.saveIP();
5248
5249 BasicBlock *InsertBlock = Loc.IP.getBlock();
5250 BasicBlock *ContinuationBlock =
5251 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
5252 InsertBlock->getTerminator()->eraseFromParent();
5253
5254 // Create and populate array of type-erased pointers to private reduction
5255 // values.
5256 unsigned NumReductions = ReductionInfos.size();
5257 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5258 Builder.SetInsertPoint(AllocaIP.getBlock()->getTerminator());
5259 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5260
5261 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
5262
5263 for (auto En : enumerate(ReductionInfos)) {
5264 unsigned Index = En.index();
5265 const ReductionInfo &RI = En.value();
5266 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5267 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5268 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5269 }
5270
5271 // Emit a call to the runtime function that orchestrates the reduction.
5272 // Declare the reduction function in the process.
5273 Type *IndexTy = Builder.getIndexTy(
5274 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5275 Function *Func = Builder.GetInsertBlock()->getParent();
5276 Module *Module = Func->getParent();
5277 uint32_t SrcLocStrSize;
5278 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5279 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5280 return RI.AtomicReductionGen;
5281 });
5282 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5283 CanGenerateAtomic
5284 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5285 : IdentFlag(0));
5286 Value *ThreadId = getOrCreateThreadID(Ident);
5287 Constant *NumVariables = Builder.getInt32(NumReductions);
5288 const DataLayout &DL = Module->getDataLayout();
5289 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5290 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5291 Function *ReductionFunc = getFreshReductionFunc(*Module);
5292 Value *Lock = getOMPCriticalRegionLock(".reduction");
5294 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5295 : RuntimeFunction::OMPRTL___kmpc_reduce);
5296 CallInst *ReduceCall =
5297 createRuntimeFunctionCall(ReduceFunc,
5298 {Ident, ThreadId, NumVariables, RedArraySize,
5299 RedArray, ReductionFunc, Lock},
5300 "reduce");
5301
5302 // Create final reduction entry blocks for the atomic and non-atomic case.
5303 // Emit IR that dispatches control flow to one of the blocks based on the
5304 // reduction supporting the atomic mode.
5305 BasicBlock *NonAtomicRedBlock =
5306 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5307 BasicBlock *AtomicRedBlock =
5308 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5309 SwitchInst *Switch =
5310 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5311 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5312 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5313
5314 // Populate the non-atomic reduction using the elementwise reduction function.
5315 // This loads the elements from the global and private variables and reduces
5316 // them before storing back the result to the global variable.
5317 Builder.SetInsertPoint(NonAtomicRedBlock);
5318 for (auto En : enumerate(ReductionInfos)) {
5319 const ReductionInfo &RI = En.value();
5321 // We have one less load for by-ref case because that load is now inside of
5322 // the reduction region
5323 Value *RedValue = RI.Variable;
5324 if (!IsByRef[En.index()]) {
5325 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5326 "red.value." + Twine(En.index()));
5327 }
5328 Value *PrivateRedValue =
5329 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5330 "red.private.value." + Twine(En.index()));
5331 Value *Reduced;
5332 InsertPointOrErrorTy AfterIP =
5333 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5334 if (!AfterIP)
5335 return AfterIP.takeError();
5336 Builder.restoreIP(*AfterIP);
5337
5338 if (!Builder.GetInsertBlock())
5339 return InsertPointTy();
5340 // for by-ref case, the load is inside of the reduction region
5341 if (!IsByRef[En.index()])
5342 Builder.CreateStore(Reduced, RI.Variable);
5343 }
5344 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5345 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5346 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5347 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5348 Builder.CreateBr(ContinuationBlock);
5349
5350 // Populate the atomic reduction using the atomic elementwise reduction
5351 // function. There are no loads/stores here because they will be happening
5352 // inside the atomic elementwise reduction.
5353 Builder.SetInsertPoint(AtomicRedBlock);
5354 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5355 for (const ReductionInfo &RI : ReductionInfos) {
5357 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5358 if (!AfterIP)
5359 return AfterIP.takeError();
5360 Builder.restoreIP(*AfterIP);
5361 if (!Builder.GetInsertBlock())
5362 return InsertPointTy();
5363 }
5364 Builder.CreateBr(ContinuationBlock);
5365 } else {
5366 Builder.CreateUnreachable();
5367 }
5368
5369 // Populate the outlined reduction function using the elementwise reduction
5370 // function. Partial values are extracted from the type-erased array of
5371 // pointers to private variables.
5372 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5373 IsByRef, /*isGPU=*/false);
5374 if (Err)
5375 return Err;
5376
5377 if (!Builder.GetInsertBlock())
5378 return InsertPointTy();
5379
5380 Builder.SetInsertPoint(ContinuationBlock);
5381 return Builder.saveIP();
5382}
5383
5386 BodyGenCallbackTy BodyGenCB,
5387 FinalizeCallbackTy FiniCB) {
5388 if (!updateToLocation(Loc))
5389 return Loc.IP;
5390
5391 Directive OMPD = Directive::OMPD_master;
5392 uint32_t SrcLocStrSize;
5393 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5394 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5395 Value *ThreadId = getOrCreateThreadID(Ident);
5396 Value *Args[] = {Ident, ThreadId};
5397
5398 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5399 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5400
5401 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5402 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5403
5404 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5405 /*Conditional*/ true, /*hasFinalize*/ true);
5406}
5407
5410 BodyGenCallbackTy BodyGenCB,
5411 FinalizeCallbackTy FiniCB, Value *Filter) {
5412 if (!updateToLocation(Loc))
5413 return Loc.IP;
5414
5415 Directive OMPD = Directive::OMPD_masked;
5416 uint32_t SrcLocStrSize;
5417 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5418 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5419 Value *ThreadId = getOrCreateThreadID(Ident);
5420 Value *Args[] = {Ident, ThreadId, Filter};
5421 Value *ArgsEnd[] = {Ident, ThreadId};
5422
5423 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5424 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5425
5426 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5427 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5428
5429 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5430 /*Conditional*/ true, /*hasFinalize*/ true);
5431}
5432
5434 llvm::FunctionCallee Callee,
5436 const llvm::Twine &Name) {
5437 llvm::CallInst *Call = Builder.CreateCall(
5438 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5439 Call->setDoesNotThrow();
5440 return Call;
5441}
5442
5443// Expects input basic block is dominated by BeforeScanBB.
5444// Once Scan directive is encountered, the code after scan directive should be
5445// dominated by AfterScanBB. Scan directive splits the code sequence to
5446// scan and input phase. Based on whether inclusive or exclusive
5447// clause is used in the scan directive and whether input loop or scan loop
5448// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5449// input loop and second is the scan loop. The code generated handles only
5450// inclusive scans now.
5452 const LocationDescription &Loc, InsertPointTy AllocaIP,
5453 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5454 bool IsInclusive, ScanInfo *ScanRedInfo) {
5455 if (ScanRedInfo->OMPFirstScanLoop) {
5456 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5457 ScanVarsType, ScanRedInfo);
5458 if (Err)
5459 return Err;
5460 }
5461 if (!updateToLocation(Loc))
5462 return Loc.IP;
5463
5464 llvm::Value *IV = ScanRedInfo->IV;
5465
5466 if (ScanRedInfo->OMPFirstScanLoop) {
5467 // Emit buffer[i] = red; at the end of the input phase.
5468 for (size_t i = 0; i < ScanVars.size(); i++) {
5469 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5470 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5471 Type *DestTy = ScanVarsType[i];
5472 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5473 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5474
5475 Builder.CreateStore(Src, Val);
5476 }
5477 }
5478 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5479 emitBlock(ScanRedInfo->OMPScanDispatch,
5480 Builder.GetInsertBlock()->getParent());
5481
5482 if (!ScanRedInfo->OMPFirstScanLoop) {
5483 IV = ScanRedInfo->IV;
5484 // Emit red = buffer[i]; at the entrance to the scan phase.
5485 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5486 for (size_t i = 0; i < ScanVars.size(); i++) {
5487 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5488 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5489 Type *DestTy = ScanVarsType[i];
5490 Value *SrcPtr =
5491 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5492 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5493 Builder.CreateStore(Src, ScanVars[i]);
5494 }
5495 }
5496
5497 // TODO: Update it to CreateBr and remove dead blocks
5498 llvm::Value *CmpI = Builder.getInt1(true);
5499 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5500 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5501 ScanRedInfo->OMPAfterScanBlock);
5502 } else {
5503 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5504 ScanRedInfo->OMPBeforeScanBlock);
5505 }
5506 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5507 Builder.GetInsertBlock()->getParent());
5508 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5509 return Builder.saveIP();
5510}
5511
5512Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5513 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5514 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5515
5516 Builder.restoreIP(AllocaIP);
5517 // Create the shared pointer at alloca IP.
5518 for (size_t i = 0; i < ScanVars.size(); i++) {
5519 llvm::Value *BuffPtr =
5520 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5521 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5522 }
5523
5524 // Allocate temporary buffer by master thread
5525 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5526 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5527 Builder.restoreIP(CodeGenIP);
5528 Value *AllocSpan =
5529 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5530 for (size_t i = 0; i < ScanVars.size(); i++) {
5531 Type *IntPtrTy = Builder.getInt32Ty();
5532 Constant *Allocsize = ConstantExpr::getSizeOf(ScanVarsType[i]);
5533 Allocsize = ConstantExpr::getTruncOrBitCast(Allocsize, IntPtrTy);
5534 Value *Buff = Builder.CreateMalloc(IntPtrTy, ScanVarsType[i], Allocsize,
5535 AllocSpan, nullptr, "arr");
5536 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5537 }
5538 return Error::success();
5539 };
5540 // TODO: Perform finalization actions for variables. This has to be
5541 // called for variables which have destructors/finalizers.
5542 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5543
5544 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5545 llvm::Value *FilterVal = Builder.getInt32(0);
5547 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5548
5549 if (!AfterIP)
5550 return AfterIP.takeError();
5551 Builder.restoreIP(*AfterIP);
5552 BasicBlock *InputBB = Builder.GetInsertBlock();
5553 if (InputBB->hasTerminator())
5554 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
5555 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5556 if (!AfterIP)
5557 return AfterIP.takeError();
5558 Builder.restoreIP(*AfterIP);
5559
5560 return Error::success();
5561}
5562
5563Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5564 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5565 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5566 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5567 Builder.restoreIP(CodeGenIP);
5568 for (ReductionInfo RedInfo : ReductionInfos) {
5569 Value *PrivateVar = RedInfo.PrivateVariable;
5570 Value *OrigVar = RedInfo.Variable;
5571 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5572 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5573
5574 Type *SrcTy = RedInfo.ElementType;
5575 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5576 "arrayOffset");
5577 Value *Src = Builder.CreateLoad(SrcTy, Val);
5578
5579 Builder.CreateStore(Src, OrigVar);
5580 Builder.CreateFree(Buff);
5581 }
5582 return Error::success();
5583 };
5584 // TODO: Perform finalization actions for variables. This has to be
5585 // called for variables which have destructors/finalizers.
5586 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5587
5588 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5589 Builder.SetInsertPoint(TI);
5590 else
5591 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5592
5593 llvm::Value *FilterVal = Builder.getInt32(0);
5595 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5596
5597 if (!AfterIP)
5598 return AfterIP.takeError();
5599 Builder.restoreIP(*AfterIP);
5600 BasicBlock *InputBB = Builder.GetInsertBlock();
5601 if (InputBB->hasTerminator())
5602 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
5603 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5604 if (!AfterIP)
5605 return AfterIP.takeError();
5606 Builder.restoreIP(*AfterIP);
5607 return Error::success();
5608}
5609
5611 const LocationDescription &Loc,
5613 ScanInfo *ScanRedInfo) {
5614
5615 if (!updateToLocation(Loc))
5616 return Loc.IP;
5617 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5618 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5619 Builder.restoreIP(CodeGenIP);
5620 Function *CurFn = Builder.GetInsertBlock()->getParent();
5621 // for (int k = 0; k <= ceil(log2(n)); ++k)
5622 llvm::BasicBlock *LoopBB =
5623 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5624 llvm::BasicBlock *ExitBB =
5625 splitBB(Builder, false, "omp.outer.log.scan.exit");
5627 Builder.GetInsertBlock()->getModule(),
5628 (llvm::Intrinsic::ID)llvm::Intrinsic::log2, Builder.getDoubleTy());
5629 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5630 llvm::Value *Arg =
5631 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5632 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5634 Builder.GetInsertBlock()->getModule(),
5635 (llvm::Intrinsic::ID)llvm::Intrinsic::ceil, Builder.getDoubleTy());
5636 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5637 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5638 llvm::Value *NMin1 = Builder.CreateNUWSub(
5639 ScanRedInfo->Span,
5640 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5641 Builder.SetInsertPoint(InputBB);
5642 Builder.CreateBr(LoopBB);
5643 emitBlock(LoopBB, CurFn);
5644 Builder.SetInsertPoint(LoopBB);
5645
5646 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5647 // size pow2k = 1;
5648 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5649 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5650 InputBB);
5651 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5652 InputBB);
5653 // for (size i = n - 1; i >= 2 ^ k; --i)
5654 // tmp[i] op= tmp[i-pow2k];
5655 llvm::BasicBlock *InnerLoopBB =
5656 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5657 llvm::BasicBlock *InnerExitBB =
5658 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5659 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5660 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5661 emitBlock(InnerLoopBB, CurFn);
5662 Builder.SetInsertPoint(InnerLoopBB);
5663 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5664 IVal->addIncoming(NMin1, LoopBB);
5665 for (ReductionInfo RedInfo : ReductionInfos) {
5666 Value *ReductionVal = RedInfo.PrivateVariable;
5667 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5668 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5669 Type *DestTy = RedInfo.ElementType;
5670 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5671 Value *LHSPtr =
5672 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5673 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5674 Value *RHSPtr =
5675 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5676 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5677 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5678 llvm::Value *Result;
5679 InsertPointOrErrorTy AfterIP =
5680 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5681 if (!AfterIP)
5682 return AfterIP.takeError();
5683 Builder.CreateStore(Result, LHSPtr);
5684 }
5685 llvm::Value *NextIVal = Builder.CreateNUWSub(
5686 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5687 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5688 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5689 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5690 emitBlock(InnerExitBB, CurFn);
5691 llvm::Value *Next = Builder.CreateNUWAdd(
5692 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5693 Counter->addIncoming(Next, Builder.GetInsertBlock());
5694 // pow2k <<= 1;
5695 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5696 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5697 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5698 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5699 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5700 return Error::success();
5701 };
5702
5703 // TODO: Perform finalization actions for variables. This has to be
5704 // called for variables which have destructors/finalizers.
5705 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5706
5707 llvm::Value *FilterVal = Builder.getInt32(0);
5709 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5710
5711 if (!AfterIP)
5712 return AfterIP.takeError();
5713 Builder.restoreIP(*AfterIP);
5714 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5715
5716 if (!AfterIP)
5717 return AfterIP.takeError();
5718 Builder.restoreIP(*AfterIP);
5719 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5720 if (Err)
5721 return Err;
5722
5723 return AfterIP;
5724}
5725
5726Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5727 llvm::function_ref<Error()> InputLoopGen,
5728 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5729 ScanInfo *ScanRedInfo) {
5730
5731 {
5732 // Emit loop with input phase:
5733 // for (i: 0..<num_iters>) {
5734 // <input phase>;
5735 // buffer[i] = red;
5736 // }
5737 ScanRedInfo->OMPFirstScanLoop = true;
5738 Error Err = InputLoopGen();
5739 if (Err)
5740 return Err;
5741 }
5742 {
5743 // Emit loop with scan phase:
5744 // for (i: 0..<num_iters>) {
5745 // red = buffer[i];
5746 // <scan phase>;
5747 // }
5748 ScanRedInfo->OMPFirstScanLoop = false;
5749 Error Err = ScanLoopGen(Builder.saveIP());
5750 if (Err)
5751 return Err;
5752 }
5753 return Error::success();
5754}
5755
5756void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5757 Function *Fun = Builder.GetInsertBlock()->getParent();
5758 ScanRedInfo->OMPScanDispatch =
5759 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5760 ScanRedInfo->OMPAfterScanBlock =
5761 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5762 ScanRedInfo->OMPBeforeScanBlock =
5763 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5764 ScanRedInfo->OMPScanLoopExit =
5765 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5766}
5768 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5769 BasicBlock *PostInsertBefore, const Twine &Name) {
5770 Module *M = F->getParent();
5771 LLVMContext &Ctx = M->getContext();
5772 Type *IndVarTy = TripCount->getType();
5773
5774 // Create the basic block structure.
5775 BasicBlock *Preheader =
5776 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5777 BasicBlock *Header =
5778 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5779 BasicBlock *Cond =
5780 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5781 BasicBlock *Body =
5782 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5783 BasicBlock *Latch =
5784 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5785 BasicBlock *Exit =
5786 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5787 BasicBlock *After =
5788 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5789
5790 // Use specified DebugLoc for new instructions.
5791 Builder.SetCurrentDebugLocation(DL);
5792
5793 Builder.SetInsertPoint(Preheader);
5794 Builder.CreateBr(Header);
5795
5796 Builder.SetInsertPoint(Header);
5797 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5798 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5799 Builder.CreateBr(Cond);
5800
5801 Builder.SetInsertPoint(Cond);
5802 Value *Cmp =
5803 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5804 Builder.CreateCondBr(Cmp, Body, Exit);
5805
5806 Builder.SetInsertPoint(Body);
5807 Builder.CreateBr(Latch);
5808
5809 Builder.SetInsertPoint(Latch);
5810 Value *Next = Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5811 "omp_" + Name + ".next", /*HasNUW=*/true);
5812 Builder.CreateBr(Header);
5813 IndVarPHI->addIncoming(Next, Latch);
5814
5815 Builder.SetInsertPoint(Exit);
5816 Builder.CreateBr(After);
5817
5818 // Remember and return the canonical control flow.
5819 LoopInfos.emplace_front();
5820 CanonicalLoopInfo *CL = &LoopInfos.front();
5821
5822 CL->Header = Header;
5823 CL->Cond = Cond;
5824 CL->Latch = Latch;
5825 CL->Exit = Exit;
5826
5827#ifndef NDEBUG
5828 CL->assertOK();
5829#endif
5830 return CL;
5831}
5832
5835 LoopBodyGenCallbackTy BodyGenCB,
5836 Value *TripCount, const Twine &Name) {
5837 BasicBlock *BB = Loc.IP.getBlock();
5838 BasicBlock *NextBB = BB->getNextNode();
5839
5840 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5841 NextBB, NextBB, Name);
5842 BasicBlock *After = CL->getAfter();
5843
5844 // If location is not set, don't connect the loop.
5845 if (updateToLocation(Loc)) {
5846 // Split the loop at the insertion point: Branch to the preheader and move
5847 // every following instruction to after the loop (the After BB). Also, the
5848 // new successor is the loop's after block.
5849 spliceBB(Builder, After, /*CreateBranch=*/false);
5850 Builder.CreateBr(CL->getPreheader());
5851 }
5852
5853 // Emit the body content. We do it after connecting the loop to the CFG to
5854 // avoid that the callback encounters degenerate BBs.
5855 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5856 return Err;
5857
5858#ifndef NDEBUG
5859 CL->assertOK();
5860#endif
5861 return CL;
5862}
5863
5865 ScanInfos.emplace_front();
5866 ScanInfo *Result = &ScanInfos.front();
5867 return Result;
5868}
5869
5873 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5874 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5875 LocationDescription ComputeLoc =
5876 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5877 updateToLocation(ComputeLoc);
5878
5880
5882 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5883 ScanRedInfo->Span = TripCount;
5884 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5885 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5886
5887 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5888 Builder.restoreIP(CodeGenIP);
5889 ScanRedInfo->IV = IV;
5890 createScanBBs(ScanRedInfo);
5891 BasicBlock *InputBlock = Builder.GetInsertBlock();
5892 Instruction *Terminator = InputBlock->getTerminator();
5893 assert(Terminator->getNumSuccessors() == 1);
5894 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5895 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5896 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5897 Builder.GetInsertBlock()->getParent());
5898 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5899 emitBlock(ScanRedInfo->OMPScanLoopExit,
5900 Builder.GetInsertBlock()->getParent());
5901 Builder.CreateBr(ContinueBlock);
5902 Builder.SetInsertPoint(
5903 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5904 return BodyGenCB(Builder.saveIP(), IV);
5905 };
5906
5907 const auto &&InputLoopGen = [&]() -> Error {
5909 Builder.saveIP(), BodyGen, Start, Stop, Step, IsSigned, InclusiveStop,
5910 ComputeIP, Name, true, ScanRedInfo);
5911 if (!LoopInfo)
5912 return LoopInfo.takeError();
5913 Result.push_back(*LoopInfo);
5914 Builder.restoreIP((*LoopInfo)->getAfterIP());
5915 return Error::success();
5916 };
5917 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5919 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5920 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5921 if (!LoopInfo)
5922 return LoopInfo.takeError();
5923 Result.push_back(*LoopInfo);
5924 Builder.restoreIP((*LoopInfo)->getAfterIP());
5925 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5926 return Error::success();
5927 };
5928 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5929 if (Err)
5930 return Err;
5931 return Result;
5932}
5933
5935 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5936 bool IsSigned, bool InclusiveStop, const Twine &Name) {
5937
5938 // Consider the following difficulties (assuming 8-bit signed integers):
5939 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
5940 // DO I = 1, 100, 50
5941 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
5942 // DO I = 100, 0, -128
5943
5944 // Start, Stop and Step must be of the same integer type.
5945 auto *IndVarTy = cast<IntegerType>(Start->getType());
5946 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
5947 assert(IndVarTy == Step->getType() && "Step type mismatch");
5948
5950
5951 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
5952 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
5953
5954 // Like Step, but always positive.
5955 Value *Incr = Step;
5956
5957 // Distance between Start and Stop; always positive.
5958 Value *Span;
5959
5960 // Condition whether there are no iterations are executed at all, e.g. because
5961 // UB < LB.
5962 Value *ZeroCmp;
5963
5964 if (IsSigned) {
5965 // Ensure that increment is positive. If not, negate and invert LB and UB.
5966 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
5967 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
5968 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
5969 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
5970 Span = Builder.CreateSub(UB, LB, "", false, true);
5971 ZeroCmp = Builder.CreateICmp(
5972 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
5973 } else {
5974 Span = Builder.CreateSub(Stop, Start, "", true);
5975 ZeroCmp = Builder.CreateICmp(
5976 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
5977 }
5978
5979 Value *CountIfLooping;
5980 if (InclusiveStop) {
5981 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
5982 } else {
5983 // Avoid incrementing past stop since it could overflow.
5984 Value *CountIfTwo = Builder.CreateAdd(
5985 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
5986 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
5987 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
5988 }
5989
5990 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
5991 "omp_" + Name + ".tripcount");
5992}
5993
5996 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5997 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
5998 ScanInfo *ScanRedInfo) {
5999 LocationDescription ComputeLoc =
6000 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6001
6003 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6004
6005 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6006 Builder.restoreIP(CodeGenIP);
6007 Value *Span = Builder.CreateMul(IV, Step);
6008 Value *IndVar = Builder.CreateAdd(Span, Start);
6009 if (InScan)
6010 ScanRedInfo->IV = IndVar;
6011 return BodyGenCB(Builder.saveIP(), IndVar);
6012 };
6013 LocationDescription LoopLoc =
6014 ComputeIP.isSet()
6015 ? Loc
6016 : LocationDescription(Builder.saveIP(),
6017 Builder.getCurrentDebugLocation());
6018 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6019}
6020
6021// Returns an LLVM function to call for initializing loop bounds using OpenMP
6022// static scheduling for composite `distribute parallel for` depending on
6023// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6024// integers as unsigned similarly to CanonicalLoopInfo.
6025static FunctionCallee
6027 OpenMPIRBuilder &OMPBuilder) {
6028 unsigned Bitwidth = Ty->getIntegerBitWidth();
6029 if (Bitwidth == 32)
6030 return OMPBuilder.getOrCreateRuntimeFunction(
6031 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6032 if (Bitwidth == 64)
6033 return OMPBuilder.getOrCreateRuntimeFunction(
6034 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6035 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6036}
6037
6038// Returns an LLVM function to call for initializing loop bounds using OpenMP
6039// static scheduling depending on `type`. Only i32 and i64 are supported by the
6040// runtime. Always interpret integers as unsigned similarly to
6041// CanonicalLoopInfo.
6043 OpenMPIRBuilder &OMPBuilder) {
6044 unsigned Bitwidth = Ty->getIntegerBitWidth();
6045 if (Bitwidth == 32)
6046 return OMPBuilder.getOrCreateRuntimeFunction(
6047 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6048 if (Bitwidth == 64)
6049 return OMPBuilder.getOrCreateRuntimeFunction(
6050 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6051 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6052}
6053
6054OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6055 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6056 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6057 OMPScheduleType DistScheduleSchedType) {
6058 assert(CLI->isValid() && "Requires a valid canonical loop");
6059 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6060 "Require dedicated allocate IP");
6061
6062 // Set up the source location value for OpenMP runtime.
6063 Builder.restoreIP(CLI->getPreheaderIP());
6064 Builder.SetCurrentDebugLocation(DL);
6065
6066 uint32_t SrcLocStrSize;
6067 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6069 switch (LoopType) {
6070 case WorksharingLoopType::ForStaticLoop:
6071 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6072 break;
6073 case WorksharingLoopType::DistributeStaticLoop:
6074 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6075 break;
6076 case WorksharingLoopType::DistributeForStaticLoop:
6077 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6078 break;
6079 }
6080 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6081
6082 // Declare useful OpenMP runtime functions.
6083 Value *IV = CLI->getIndVar();
6084 Type *IVTy = IV->getType();
6085 FunctionCallee StaticInit =
6086 LoopType == WorksharingLoopType::DistributeForStaticLoop
6087 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6088 : getKmpcForStaticInitForType(IVTy, M, *this);
6089 FunctionCallee StaticFini =
6090 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6091
6092 // Allocate space for computed loop bounds as expected by the "init" function.
6093 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6094
6095 Type *I32Type = Type::getInt32Ty(M.getContext());
6096 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6097 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6098 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6099 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6100 CLI->setLastIter(PLastIter);
6101
6102 // At the end of the preheader, prepare for calling the "init" function by
6103 // storing the current loop bounds into the allocated space. A canonical loop
6104 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6105 // and produces an inclusive upper bound.
6106 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6107 Constant *Zero = ConstantInt::get(IVTy, 0);
6108 Constant *One = ConstantInt::get(IVTy, 1);
6109 Builder.CreateStore(Zero, PLowerBound);
6110 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6111 Builder.CreateStore(UpperBound, PUpperBound);
6112 Builder.CreateStore(One, PStride);
6113
6114 Value *ThreadNum =
6115 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6116
6117 OMPScheduleType SchedType =
6118 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6119 ? OMPScheduleType::OrderedDistribute
6121 Constant *SchedulingType =
6122 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6123
6124 // Call the "init" function and update the trip count of the loop with the
6125 // value it produced.
6126 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6127 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6128 this](Value *SchedulingType, auto &Builder) {
6129 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6130 PLowerBound, PUpperBound});
6131 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6132 Value *PDistUpperBound =
6133 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6134 Args.push_back(PDistUpperBound);
6135 }
6136 Args.append({PStride, One, Zero});
6137 createRuntimeFunctionCall(StaticInit, Args);
6138 };
6139 BuildInitCall(SchedulingType, Builder);
6140 if (HasDistSchedule &&
6141 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6142 Constant *DistScheduleSchedType = ConstantInt::get(
6143 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6144 // We want to emit a second init function call for the dist_schedule clause
6145 // to the Distribute construct. This should only be done however if a
6146 // Workshare Loop is nested within a Distribute Construct
6147 BuildInitCall(DistScheduleSchedType, Builder);
6148 }
6149 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6150 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6151 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6152 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6153 CLI->setTripCount(TripCount);
6154
6155 // Update all uses of the induction variable except the one in the condition
6156 // block that compares it with the actual upper bound, and the increment in
6157 // the latch block.
6158
6159 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6160 Builder.SetInsertPoint(CLI->getBody(),
6161 CLI->getBody()->getFirstInsertionPt());
6162 Builder.SetCurrentDebugLocation(DL);
6163 return Builder.CreateAdd(OldIV, LowerBound);
6164 });
6165
6166 // In the "exit" block, call the "fini" function.
6167 Builder.SetInsertPoint(CLI->getExit(),
6168 CLI->getExit()->getTerminator()->getIterator());
6169 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6170
6171 // Add the barrier if requested.
6172 if (NeedsBarrier) {
6173 InsertPointOrErrorTy BarrierIP =
6175 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6176 /* CheckCancelFlag */ false);
6177 if (!BarrierIP)
6178 return BarrierIP.takeError();
6179 }
6180
6181 InsertPointTy AfterIP = CLI->getAfterIP();
6182 CLI->invalidate();
6183
6184 return AfterIP;
6185}
6186
6187static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6188 LoopInfo &LI);
6189static void addLoopMetadata(CanonicalLoopInfo *Loop,
6190 ArrayRef<Metadata *> Properties);
6191
6193 LLVMContext &Ctx, Loop *Loop,
6195 SmallVector<Metadata *> &LoopMDList) {
6196 SmallSet<BasicBlock *, 8> Reachable;
6197
6198 // Get the basic blocks from the loop in which memref instructions
6199 // can be found.
6200 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6201 // preferably without running any passes.
6202 for (BasicBlock *Block : Loop->getBlocks()) {
6203 if (Block == CLI->getCond() || Block == CLI->getHeader())
6204 continue;
6205 Reachable.insert(Block);
6206 }
6207
6208 // Add access group metadata to memory-access instructions.
6209 MDNode *AccessGroup = MDNode::getDistinct(Ctx, {});
6210 for (BasicBlock *BB : Reachable)
6211 addAccessGroupMetadata(BB, AccessGroup, LoopInfo);
6212 // TODO: If the loop has existing parallel access metadata, have
6213 // to combine two lists.
6214 LoopMDList.push_back(MDNode::get(
6215 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6216}
6217
6219OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6220 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6221 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6222 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6223 assert(CLI->isValid() && "Requires a valid canonical loop");
6224 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6225
6226 LLVMContext &Ctx = CLI->getFunction()->getContext();
6227 Value *IV = CLI->getIndVar();
6228 Value *OrigTripCount = CLI->getTripCount();
6229 Type *IVTy = IV->getType();
6230 assert(IVTy->getIntegerBitWidth() <= 64 &&
6231 "Max supported tripcount bitwidth is 64 bits");
6232 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6233 : Type::getInt64Ty(Ctx);
6234 Type *I32Type = Type::getInt32Ty(M.getContext());
6235 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6236 Constant *One = ConstantInt::get(InternalIVTy, 1);
6237
6238 Function *F = CLI->getFunction();
6239 // Blocks must have terminators.
6240 // FIXME: Don't run analyses on incomplete/invalid IR.
6241 SmallVector<Instruction *> UIs;
6242 for (BasicBlock &BB : *F)
6243 if (!BB.hasTerminator())
6244 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6246 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6247 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6248 LoopAnalysis LIA;
6249 LoopInfo &&LI = LIA.run(*F, FAM);
6250 for (Instruction *I : UIs)
6251 I->eraseFromParent();
6252 Loop *L = LI.getLoopFor(CLI->getHeader());
6253 SmallVector<Metadata *> LoopMDList;
6254 if (ChunkSize || DistScheduleChunkSize)
6255 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6256 addLoopMetadata(CLI, LoopMDList);
6257
6258 // Declare useful OpenMP runtime functions.
6259 FunctionCallee StaticInit =
6260 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6261 FunctionCallee StaticFini =
6262 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6263
6264 // Allocate space for computed loop bounds as expected by the "init" function.
6265 Builder.restoreIP(AllocaIP);
6266 Builder.SetCurrentDebugLocation(DL);
6267 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6268 Value *PLowerBound =
6269 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6270 Value *PUpperBound =
6271 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6272 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6273 CLI->setLastIter(PLastIter);
6274
6275 // Set up the source location value for the OpenMP runtime.
6276 Builder.restoreIP(CLI->getPreheaderIP());
6277 Builder.SetCurrentDebugLocation(DL);
6278
6279 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6280 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6281 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6282 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6283 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6284 "distschedulechunksize");
6285 Value *CastedTripCount =
6286 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6287
6288 Constant *SchedulingType =
6289 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6290 Constant *DistSchedulingType =
6291 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6292 Builder.CreateStore(Zero, PLowerBound);
6293 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6294 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6295 Value *UpperBound =
6296 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6297 Builder.CreateStore(UpperBound, PUpperBound);
6298 Builder.CreateStore(One, PStride);
6299
6300 // Call the "init" function and update the trip count of the loop with the
6301 // value it produced.
6302 uint32_t SrcLocStrSize;
6303 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6304 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6305 if (DistScheduleSchedType != OMPScheduleType::None) {
6306 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6307 }
6308 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6309 Value *ThreadNum =
6310 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6311 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6312 PUpperBound, PStride, One,
6313 this](Value *SchedulingType, Value *ChunkSize,
6314 auto &Builder) {
6316 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6317 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6318 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6319 /*pstride=*/PStride, /*incr=*/One,
6320 /*chunk=*/ChunkSize});
6321 };
6322 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6323 if (DistScheduleSchedType != OMPScheduleType::None &&
6324 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6325 SchedType != OMPScheduleType::OrderedDistribute) {
6326 // We want to emit a second init function call for the dist_schedule clause
6327 // to the Distribute construct. This should only be done however if a
6328 // Workshare Loop is nested within a Distribute Construct
6329 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6330 }
6331
6332 // Load values written by the "init" function.
6333 Value *FirstChunkStart =
6334 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6335 Value *FirstChunkStop =
6336 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6337 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6338 Value *ChunkRange =
6339 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6340 Value *NextChunkStride =
6341 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6342
6343 // Create outer "dispatch" loop for enumerating the chunks.
6344 BasicBlock *DispatchEnter = splitBB(Builder, true);
6345 Value *DispatchCounter;
6346
6347 // It is safe to assume this didn't return an error because the callback
6348 // passed into createCanonicalLoop is the only possible error source, and it
6349 // always returns success.
6350 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6351 {Builder.saveIP(), DL},
6352 [&](InsertPointTy BodyIP, Value *Counter) {
6353 DispatchCounter = Counter;
6354 return Error::success();
6355 },
6356 FirstChunkStart, CastedTripCount, NextChunkStride,
6357 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6358 "dispatch"));
6359
6360 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6361 // not have to preserve the canonical invariant.
6362 BasicBlock *DispatchBody = DispatchCLI->getBody();
6363 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6364 BasicBlock *DispatchExit = DispatchCLI->getExit();
6365 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6366 DispatchCLI->invalidate();
6367
6368 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6369 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6370 redirectTo(CLI->getExit(), DispatchLatch, DL);
6371 redirectTo(DispatchBody, DispatchEnter, DL);
6372
6373 // Prepare the prolog of the chunk loop.
6374 Builder.restoreIP(CLI->getPreheaderIP());
6375 Builder.SetCurrentDebugLocation(DL);
6376
6377 // Compute the number of iterations of the chunk loop.
6378 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6379 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6380 Value *IsLastChunk =
6381 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6382 Value *CountUntilOrigTripCount =
6383 Builder.CreateSub(CastedTripCount, DispatchCounter);
6384 Value *ChunkTripCount = Builder.CreateSelect(
6385 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6386 Value *BackcastedChunkTC =
6387 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6388 CLI->setTripCount(BackcastedChunkTC);
6389
6390 // Update all uses of the induction variable except the one in the condition
6391 // block that compares it with the actual upper bound, and the increment in
6392 // the latch block.
6393 Value *BackcastedDispatchCounter =
6394 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6395 CLI->mapIndVar([&](Instruction *) -> Value * {
6396 Builder.restoreIP(CLI->getBodyIP());
6397 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6398 });
6399
6400 // In the "exit" block, call the "fini" function.
6401 Builder.SetInsertPoint(DispatchExit, DispatchExit->getFirstInsertionPt());
6402 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6403
6404 // Add the barrier if requested.
6405 if (NeedsBarrier) {
6406 InsertPointOrErrorTy AfterIP =
6407 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6408 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6409 if (!AfterIP)
6410 return AfterIP.takeError();
6411 }
6412
6413#ifndef NDEBUG
6414 // Even though we currently do not support applying additional methods to it,
6415 // the chunk loop should remain a canonical loop.
6416 CLI->assertOK();
6417#endif
6418
6419 return InsertPointTy(DispatchAfter, DispatchAfter->getFirstInsertionPt());
6420}
6421
6422// Returns an LLVM function to call for executing an OpenMP static worksharing
6423// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6424// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6425static FunctionCallee
6427 WorksharingLoopType LoopType) {
6428 unsigned Bitwidth = Ty->getIntegerBitWidth();
6429 Module &M = OMPBuilder->M;
6430 switch (LoopType) {
6431 case WorksharingLoopType::ForStaticLoop:
6432 if (Bitwidth == 32)
6433 return OMPBuilder->getOrCreateRuntimeFunction(
6434 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6435 if (Bitwidth == 64)
6436 return OMPBuilder->getOrCreateRuntimeFunction(
6437 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6438 break;
6439 case WorksharingLoopType::DistributeStaticLoop:
6440 if (Bitwidth == 32)
6441 return OMPBuilder->getOrCreateRuntimeFunction(
6442 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6443 if (Bitwidth == 64)
6444 return OMPBuilder->getOrCreateRuntimeFunction(
6445 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6446 break;
6447 case WorksharingLoopType::DistributeForStaticLoop:
6448 if (Bitwidth == 32)
6449 return OMPBuilder->getOrCreateRuntimeFunction(
6450 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6451 if (Bitwidth == 64)
6452 return OMPBuilder->getOrCreateRuntimeFunction(
6453 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6454 break;
6455 }
6456 if (Bitwidth != 32 && Bitwidth != 64) {
6457 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6458 }
6459 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6460}
6461
6462// Inserts a call to proper OpenMP Device RTL function which handles
6463// loop worksharing.
6465 WorksharingLoopType LoopType,
6466 BasicBlock *InsertBlock, Value *Ident,
6467 Value *LoopBodyArg, Value *TripCount,
6468 Function &LoopBodyFn, bool NoLoop) {
6469 Type *TripCountTy = TripCount->getType();
6470 Module &M = OMPBuilder->M;
6471 IRBuilder<> &Builder = OMPBuilder->Builder;
6472 FunctionCallee RTLFn =
6473 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6474 SmallVector<Value *, 8> RealArgs;
6475 RealArgs.push_back(Ident);
6476 RealArgs.push_back(&LoopBodyFn);
6477 RealArgs.push_back(LoopBodyArg);
6478 RealArgs.push_back(TripCount);
6479 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6480 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6481 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6482 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6483 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6484 return;
6485 }
6486 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6487 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6488 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6489 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6490
6491 RealArgs.push_back(
6492 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6493 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6494 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6495 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6496 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6497 } else {
6498 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6499 }
6500
6501 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6502}
6503
6505 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6506 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6507 WorksharingLoopType LoopType, bool NoLoop) {
6508 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6509 BasicBlock *Preheader = CLI->getPreheader();
6510 Value *TripCount = CLI->getTripCount();
6511
6512 // After loop body outling, the loop body contains only set up
6513 // of loop body argument structure and the call to the outlined
6514 // loop body function. Firstly, we need to move setup of loop body args
6515 // into loop preheader.
6516 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6517 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6518
6519 // The next step is to remove the whole loop. We do not it need anymore.
6520 // That's why make an unconditional branch from loop preheader to loop
6521 // exit block
6522 Builder.restoreIP({Preheader, Preheader->end()});
6523 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6524 Preheader->getTerminator()->eraseFromParent();
6525 Builder.CreateBr(CLI->getExit());
6526
6527 // Delete dead loop blocks
6528 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6529 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6530 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6531 CleanUpInfo.EntryBB = CLI->getHeader();
6532 CleanUpInfo.ExitBB = CLI->getExit();
6533 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6534 DeleteDeadBlocks(BlocksToBeRemoved);
6535
6536 // Find the instruction which corresponds to loop body argument structure
6537 // and remove the call to loop body function instruction.
6538 Value *LoopBodyArg;
6539 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6540 assert(OutlinedFnUser &&
6541 "Expected unique undroppable user of outlined function");
6542 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6543 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6544 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6545 "Expected outlined function call to be located in loop preheader");
6546 // Check in case no argument structure has been passed.
6547 if (OutlinedFnCallInstruction->arg_size() > 1)
6548 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6549 else
6550 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6551 OutlinedFnCallInstruction->eraseFromParent();
6552
6553 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6554 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6555
6556 for (auto &ToBeDeletedItem : ToBeDeleted)
6557 ToBeDeletedItem->eraseFromParent();
6558 CLI->invalidate();
6559}
6560
6561OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6562 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6563 WorksharingLoopType LoopType, bool NoLoop) {
6564 uint32_t SrcLocStrSize;
6565 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6567 switch (LoopType) {
6568 case WorksharingLoopType::ForStaticLoop:
6569 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6570 break;
6571 case WorksharingLoopType::DistributeStaticLoop:
6572 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6573 break;
6574 case WorksharingLoopType::DistributeForStaticLoop:
6575 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6576 break;
6577 }
6578 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6579
6580 auto OI = std::make_unique<OutlineInfo>();
6581 OI->OuterAllocBB = CLI->getPreheader();
6582 Function *OuterFn = CLI->getPreheader()->getParent();
6583
6584 // Instructions which need to be deleted at the end of code generation
6585 SmallVector<Instruction *, 4> ToBeDeleted;
6586
6587 OI->OuterAllocBB = AllocaIP.getBlock();
6588
6589 // Mark the body loop as region which needs to be extracted
6590 OI->EntryBB = CLI->getBody();
6591 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6592 "omp.prelatch");
6593
6594 // Prepare loop body for extraction
6595 Builder.restoreIP({CLI->getPreheader(), CLI->getPreheader()->begin()});
6596
6597 // Insert new loop counter variable which will be used only in loop
6598 // body.
6599 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6600 Instruction *NewLoopCntLoad =
6601 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6602 // New loop counter instructions are redundant in the loop preheader when
6603 // code generation for workshare loop is finshed. That's why mark them as
6604 // ready for deletion.
6605 ToBeDeleted.push_back(NewLoopCntLoad);
6606 ToBeDeleted.push_back(NewLoopCnt);
6607
6608 // Analyse loop body region. Find all input variables which are used inside
6609 // loop body region.
6610 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6612 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6613
6614 CodeExtractorAnalysisCache CEAC(*OuterFn);
6615 CodeExtractor Extractor(Blocks,
6616 /* DominatorTree */ nullptr,
6617 /* AggregateArgs */ true,
6618 /* BlockFrequencyInfo */ nullptr,
6619 /* BranchProbabilityInfo */ nullptr,
6620 /* AssumptionCache */ nullptr,
6621 /* AllowVarArgs */ true,
6622 /* AllowAlloca */ true,
6623 /* AllocationBlock */ CLI->getPreheader(),
6624 /* DeallocationBlocks */ {},
6625 /* Suffix */ ".omp_wsloop",
6626 /* AggrArgsIn0AddrSpace */ true);
6627
6628 BasicBlock *CommonExit = nullptr;
6629 SetVector<Value *> SinkingCands, HoistingCands;
6630
6631 // Find allocas outside the loop body region which are used inside loop
6632 // body
6633 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6634
6635 // We need to model loop body region as the function f(cnt, loop_arg).
6636 // That's why we replace loop induction variable by the new counter
6637 // which will be one of loop body function argument
6639 CLI->getIndVar()->user_end());
6640 for (auto Use : Users) {
6641 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6642 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6643 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6644 }
6645 }
6646 }
6647 // Make sure that loop counter variable is not merged into loop body
6648 // function argument structure and it is passed as separate variable
6649 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6650
6651 // PostOutline CB is invoked when loop body function is outlined and
6652 // loop body is replaced by call to outlined function. We need to add
6653 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6654 // function will handle loop control logic.
6655 //
6656 OI->PostOutlineCB = [=, ToBeDeletedVec =
6657 std::move(ToBeDeleted)](Function &OutlinedFn) {
6658 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6659 LoopType, NoLoop);
6660 };
6661 addOutlineInfo(std::move(OI));
6662 return CLI->getAfterIP();
6663}
6664
6667 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6668 bool HasSimdModifier, bool HasMonotonicModifier,
6669 bool HasNonmonotonicModifier, bool HasOrderedClause,
6670 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6671 Value *DistScheduleChunkSize) {
6672 if (Config.isTargetDevice())
6673 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NoLoop);
6674 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6675 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6676 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6677
6678 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6679 OMPScheduleType::ModifierOrdered;
6680 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6681 if (HasDistSchedule) {
6682 DistScheduleSchedType = DistScheduleChunkSize
6683 ? OMPScheduleType::OrderedDistributeChunked
6684 : OMPScheduleType::OrderedDistribute;
6685 }
6686 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6687 case OMPScheduleType::BaseStatic:
6688 case OMPScheduleType::BaseDistribute:
6689 assert((!ChunkSize || !DistScheduleChunkSize) &&
6690 "No chunk size with static-chunked schedule");
6691 if (IsOrdered && !HasDistSchedule)
6692 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6693 NeedsBarrier, ChunkSize);
6694 // FIXME: Monotonicity ignored?
6695 if (DistScheduleChunkSize)
6696 return applyStaticChunkedWorkshareLoop(
6697 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6698 DistScheduleChunkSize, DistScheduleSchedType);
6699 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6700 HasDistSchedule);
6701
6702 case OMPScheduleType::BaseStaticChunked:
6703 case OMPScheduleType::BaseDistributeChunked:
6704 if (IsOrdered && !HasDistSchedule)
6705 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6706 NeedsBarrier, ChunkSize);
6707 // FIXME: Monotonicity ignored?
6708 return applyStaticChunkedWorkshareLoop(
6709 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6710 DistScheduleChunkSize, DistScheduleSchedType);
6711
6712 case OMPScheduleType::BaseRuntime:
6713 case OMPScheduleType::BaseAuto:
6714 case OMPScheduleType::BaseGreedy:
6715 case OMPScheduleType::BaseBalanced:
6716 case OMPScheduleType::BaseSteal:
6717 case OMPScheduleType::BaseRuntimeSimd:
6718 assert(!ChunkSize &&
6719 "schedule type does not support user-defined chunk sizes");
6720 [[fallthrough]];
6721 case OMPScheduleType::BaseGuidedSimd:
6722 case OMPScheduleType::BaseDynamicChunked:
6723 case OMPScheduleType::BaseGuidedChunked:
6724 case OMPScheduleType::BaseGuidedIterativeChunked:
6725 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6726 case OMPScheduleType::BaseStaticBalancedChunked:
6727 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6728 NeedsBarrier, ChunkSize);
6729
6730 default:
6731 llvm_unreachable("Unknown/unimplemented schedule kind");
6732 }
6733}
6734
6735/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6736/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6737/// the runtime. Always interpret integers as unsigned similarly to
6738/// CanonicalLoopInfo.
6739static FunctionCallee
6741 unsigned Bitwidth = Ty->getIntegerBitWidth();
6742 if (Bitwidth == 32)
6743 return OMPBuilder.getOrCreateRuntimeFunction(
6744 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6745 if (Bitwidth == 64)
6746 return OMPBuilder.getOrCreateRuntimeFunction(
6747 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6748 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6749}
6750
6751/// Returns an LLVM function to call for updating the next loop using OpenMP
6752/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6753/// the runtime. Always interpret integers as unsigned similarly to
6754/// CanonicalLoopInfo.
6755static FunctionCallee
6757 unsigned Bitwidth = Ty->getIntegerBitWidth();
6758 if (Bitwidth == 32)
6759 return OMPBuilder.getOrCreateRuntimeFunction(
6760 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6761 if (Bitwidth == 64)
6762 return OMPBuilder.getOrCreateRuntimeFunction(
6763 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6764 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6765}
6766
6767/// Returns an LLVM function to call for finalizing the dynamic loop using
6768/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6769/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6770static FunctionCallee
6772 unsigned Bitwidth = Ty->getIntegerBitWidth();
6773 if (Bitwidth == 32)
6774 return OMPBuilder.getOrCreateRuntimeFunction(
6775 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6776 if (Bitwidth == 64)
6777 return OMPBuilder.getOrCreateRuntimeFunction(
6778 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6779 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6780}
6781
6783OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6784 InsertPointTy AllocaIP,
6785 OMPScheduleType SchedType,
6786 bool NeedsBarrier, Value *Chunk) {
6787 assert(CLI->isValid() && "Requires a valid canonical loop");
6788 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6789 "Require dedicated allocate IP");
6791 "Require valid schedule type");
6792
6793 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6794 OMPScheduleType::ModifierOrdered;
6795
6796 // Set up the source location value for OpenMP runtime.
6797 Builder.SetCurrentDebugLocation(DL);
6798
6799 uint32_t SrcLocStrSize;
6800 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6801 Value *SrcLoc =
6802 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6803
6804 // Declare useful OpenMP runtime functions.
6805 Value *IV = CLI->getIndVar();
6806 Type *IVTy = IV->getType();
6807 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6808 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6809
6810 // Allocate space for computed loop bounds as expected by the "init" function.
6811 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6812 Type *I32Type = Type::getInt32Ty(M.getContext());
6813 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6814 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6815 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6816 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6817 CLI->setLastIter(PLastIter);
6818
6819 // At the end of the preheader, prepare for calling the "init" function by
6820 // storing the current loop bounds into the allocated space. A canonical loop
6821 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6822 // and produces an inclusive upper bound.
6823 BasicBlock *PreHeader = CLI->getPreheader();
6824 Builder.SetInsertPoint(PreHeader->getTerminator());
6825 Constant *One = ConstantInt::get(IVTy, 1);
6826 Builder.CreateStore(One, PLowerBound);
6827 Value *UpperBound = CLI->getTripCount();
6828 Builder.CreateStore(UpperBound, PUpperBound);
6829 Builder.CreateStore(One, PStride);
6830
6831 BasicBlock *Header = CLI->getHeader();
6832 BasicBlock *Exit = CLI->getExit();
6833 BasicBlock *Cond = CLI->getCond();
6834 BasicBlock *Latch = CLI->getLatch();
6835 InsertPointTy AfterIP = CLI->getAfterIP();
6836
6837 // The CLI will be "broken" in the code below, as the loop is no longer
6838 // a valid canonical loop.
6839
6840 if (!Chunk)
6841 Chunk = One;
6842
6843 Value *ThreadNum =
6844 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6845
6846 Constant *SchedulingType =
6847 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6848
6849 // Call the "init" function.
6850 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6851 /* LowerBound */ One, UpperBound,
6852 /* step */ One, Chunk});
6853
6854 // An outer loop around the existing one.
6855 BasicBlock *OuterCond = BasicBlock::Create(
6856 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6857 PreHeader->getParent());
6858 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6859 Builder.SetInsertPoint(OuterCond, OuterCond->getFirstInsertionPt());
6861 DynamicNext,
6862 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6863 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6864 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6865 Value *LowerBound =
6866 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6867 Builder.CreateCondBr(MoreWork, Header, Exit);
6868
6869 // Change PHI-node in loop header to use outer cond rather than preheader,
6870 // and set IV to the LowerBound.
6871 Instruction *Phi = &Header->front();
6872 auto *PI = cast<PHINode>(Phi);
6873 PI->setIncomingBlock(0, OuterCond);
6874 PI->setIncomingValue(0, LowerBound);
6875
6876 // Then set the pre-header to jump to the OuterCond
6877 Instruction *Term = PreHeader->getTerminator();
6878 auto *Br = cast<UncondBrInst>(Term);
6879 Br->setSuccessor(OuterCond);
6880
6881 // Modify the inner condition:
6882 // * Use the UpperBound returned from the DynamicNext call.
6883 // * jump to the loop outer loop when done with one of the inner loops.
6884 Builder.SetInsertPoint(Cond, Cond->getFirstInsertionPt());
6885 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
6886 Instruction *Comp = &*Builder.GetInsertPoint();
6887 auto *CI = cast<CmpInst>(Comp);
6888 CI->setOperand(1, UpperBound);
6889 // Redirect the inner exit to branch to outer condition.
6890 Instruction *Branch = &Cond->back();
6891 auto *BI = cast<CondBrInst>(Branch);
6892 assert(BI->getSuccessor(1) == Exit);
6893 BI->setSuccessor(1, OuterCond);
6894
6895 // Call the "fini" function if "ordered" is present in wsloop directive.
6896 if (Ordered) {
6897 Builder.SetInsertPoint(&Latch->back());
6898 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
6899 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
6900 }
6901
6902 // Add the barrier if requested.
6903 if (NeedsBarrier) {
6904 Builder.SetInsertPoint(&Exit->back());
6905 InsertPointOrErrorTy BarrierIP =
6907 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6908 /* CheckCancelFlag */ false);
6909 if (!BarrierIP)
6910 return BarrierIP.takeError();
6911 }
6912
6913 CLI->invalidate();
6914 return AfterIP;
6915}
6916
6917/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
6918/// after this \p OldTarget will be orphaned.
6920 BasicBlock *NewTarget, DebugLoc DL) {
6921 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
6922 redirectTo(Pred, NewTarget, DL);
6923}
6924
6926 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
6927 // We add a block to BBsToKeep iff we have proven it has an external use.
6929
6930 while (true) {
6931 bool Changed = false;
6932
6933 for (BasicBlock *BB : BBs) {
6934 if (BBsToKeep.contains(BB))
6935 continue;
6936
6937 for (Use &U : BB->uses()) {
6938 auto *UseInst = dyn_cast<Instruction>(U.getUser());
6939 if (!UseInst)
6940 continue;
6941 BasicBlock *UseBB = UseInst->getParent();
6942 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
6943 BBsToKeep.insert(BB);
6944 Changed = true;
6945 break;
6946 }
6947 }
6948 }
6949
6950 if (!Changed)
6951 break;
6952 }
6953
6955 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
6956 DeleteDeadBlocks(BBsToDelete);
6957}
6958
6959CanonicalLoopInfo *
6961 InsertPointTy ComputeIP) {
6962 assert(Loops.size() >= 1 && "At least one loop required");
6963 size_t NumLoops = Loops.size();
6964
6965 // Nothing to do if there is already just one loop.
6966 if (NumLoops == 1)
6967 return Loops.front();
6968
6969 CanonicalLoopInfo *Outermost = Loops.front();
6970 CanonicalLoopInfo *Innermost = Loops.back();
6971 BasicBlock *OrigPreheader = Outermost->getPreheader();
6972 BasicBlock *OrigAfter = Outermost->getAfter();
6973 Function *F = OrigPreheader->getParent();
6974
6975 // Loop control blocks that may become orphaned later.
6976 SmallVector<BasicBlock *, 12> OldControlBBs;
6977 OldControlBBs.reserve(6 * Loops.size());
6979 Loop->collectControlBlocks(OldControlBBs);
6980
6981 // Setup the IRBuilder for inserting the trip count computation.
6982 Builder.SetCurrentDebugLocation(DL);
6983 if (ComputeIP.isSet())
6984 Builder.restoreIP(ComputeIP);
6985 else
6986 Builder.restoreIP(Outermost->getPreheaderIP());
6987
6988 // Derive the collapsed' loop trip count.
6989 // TODO: Find common/largest indvar type.
6990 Value *CollapsedTripCount = nullptr;
6991 for (CanonicalLoopInfo *L : Loops) {
6992 assert(L->isValid() &&
6993 "All loops to collapse must be valid canonical loops");
6994 Value *OrigTripCount = L->getTripCount();
6995 if (!CollapsedTripCount) {
6996 CollapsedTripCount = OrigTripCount;
6997 continue;
6998 }
6999
7000 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7001 CollapsedTripCount =
7002 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7003 }
7004
7005 // Create the collapsed loop control flow.
7006 CanonicalLoopInfo *Result =
7007 createLoopSkeleton(DL, CollapsedTripCount, F,
7008 OrigPreheader->getNextNode(), OrigAfter, "collapsed");
7009
7010 // Build the collapsed loop body code.
7011 // Start with deriving the input loop induction variables from the collapsed
7012 // one, using a divmod scheme. To preserve the original loops' order, the
7013 // innermost loop use the least significant bits.
7014 Builder.restoreIP(Result->getBodyIP());
7015
7016 Value *Leftover = Result->getIndVar();
7017 SmallVector<Value *> NewIndVars;
7018 NewIndVars.resize(NumLoops);
7019 for (int i = NumLoops - 1; i >= 1; --i) {
7020 Value *OrigTripCount = Loops[i]->getTripCount();
7021
7022 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7023 NewIndVars[i] = NewIndVar;
7024
7025 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7026 }
7027 // Outermost loop gets all the remaining bits.
7028 NewIndVars[0] = Leftover;
7029
7030 // Construct the loop body control flow.
7031 // We progressively construct the branch structure following in direction of
7032 // the control flow, from the leading in-between code, the loop nest body, the
7033 // trailing in-between code, and rejoining the collapsed loop's latch.
7034 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7035 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7036 // its predecessors as sources.
7037 BasicBlock *ContinueBlock = Result->getBody();
7038 BasicBlock *ContinuePred = nullptr;
7039 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7040 BasicBlock *NextSrc) {
7041 if (ContinueBlock)
7042 redirectTo(ContinueBlock, Dest, DL);
7043 else
7044 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7045
7046 ContinueBlock = nullptr;
7047 ContinuePred = NextSrc;
7048 };
7049
7050 // The code before the nested loop of each level.
7051 // Because we are sinking it into the nest, it will be executed more often
7052 // that the original loop. More sophisticated schemes could keep track of what
7053 // the in-between code is and instantiate it only once per thread.
7054 for (size_t i = 0; i < NumLoops - 1; ++i)
7055 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7056
7057 // Connect the loop nest body.
7058 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7059
7060 // The code after the nested loop at each level.
7061 for (size_t i = NumLoops - 1; i > 0; --i)
7062 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7063
7064 // Connect the finished loop to the collapsed loop latch.
7065 ContinueWith(Result->getLatch(), nullptr);
7066
7067 // Replace the input loops with the new collapsed loop.
7068 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7069 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7070
7071 // Replace the input loop indvars with the derived ones.
7072 for (size_t i = 0; i < NumLoops; ++i)
7073 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7074
7075 // Remove unused parts of the input loops.
7076 removeUnusedBlocksFromParent(OldControlBBs);
7077
7078 for (CanonicalLoopInfo *L : Loops)
7079 L->invalidate();
7080
7081#ifndef NDEBUG
7082 Result->assertOK();
7083#endif
7084 return Result;
7085}
7086
7087std::vector<CanonicalLoopInfo *>
7089 ArrayRef<Value *> TileSizes) {
7090 assert(TileSizes.size() == Loops.size() &&
7091 "Must pass as many tile sizes as there are loops");
7092 int NumLoops = Loops.size();
7093 assert(NumLoops >= 1 && "At least one loop to tile required");
7094
7095 CanonicalLoopInfo *OutermostLoop = Loops.front();
7096 CanonicalLoopInfo *InnermostLoop = Loops.back();
7097 Function *F = OutermostLoop->getBody()->getParent();
7098 BasicBlock *InnerEnter = InnermostLoop->getBody();
7099 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7100
7101 // Loop control blocks that may become orphaned later.
7102 SmallVector<BasicBlock *, 12> OldControlBBs;
7103 OldControlBBs.reserve(6 * Loops.size());
7105 Loop->collectControlBlocks(OldControlBBs);
7106
7107 // Collect original trip counts and induction variable to be accessible by
7108 // index. Also, the structure of the original loops is not preserved during
7109 // the construction of the tiled loops, so do it before we scavenge the BBs of
7110 // any original CanonicalLoopInfo.
7111 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7112 for (CanonicalLoopInfo *L : Loops) {
7113 assert(L->isValid() && "All input loops must be valid canonical loops");
7114 OrigTripCounts.push_back(L->getTripCount());
7115 OrigIndVars.push_back(L->getIndVar());
7116 }
7117
7118 // Collect the code between loop headers. These may contain SSA definitions
7119 // that are used in the loop nest body. To be usable with in the innermost
7120 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7121 // these instructions may be executed more often than before the tiling.
7122 // TODO: It would be sufficient to only sink them into body of the
7123 // corresponding tile loop.
7125 for (int i = 0; i < NumLoops - 1; ++i) {
7126 CanonicalLoopInfo *Surrounding = Loops[i];
7127 CanonicalLoopInfo *Nested = Loops[i + 1];
7128
7129 BasicBlock *EnterBB = Surrounding->getBody();
7130 BasicBlock *ExitBB = Nested->getHeader();
7131 InbetweenCode.emplace_back(EnterBB, ExitBB);
7132 }
7133
7134 // Compute the trip counts of the floor loops.
7135 Builder.SetCurrentDebugLocation(DL);
7136 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7137 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7138 for (int i = 0; i < NumLoops; ++i) {
7139 Value *TileSize = TileSizes[i];
7140 Value *OrigTripCount = OrigTripCounts[i];
7141 Type *IVType = OrigTripCount->getType();
7142
7143 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7144 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7145
7146 // 0 if tripcount divides the tilesize, 1 otherwise.
7147 // 1 means we need an additional iteration for a partial tile.
7148 //
7149 // Unfortunately we cannot just use the roundup-formula
7150 // (tripcount + tilesize - 1)/tilesize
7151 // because the summation might overflow. We do not want introduce undefined
7152 // behavior when the untiled loop nest did not.
7153 Value *FloorTripOverflow =
7154 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7155
7156 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7157 Value *FloorTripCount =
7158 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7159 "omp_floor" + Twine(i) + ".tripcount", true);
7160
7161 // Remember some values for later use.
7162 FloorCompleteCount.push_back(FloorCompleteTripCount);
7163 FloorCount.push_back(FloorTripCount);
7164 FloorRems.push_back(FloorTripRem);
7165 }
7166
7167 // Generate the new loop nest, from the outermost to the innermost.
7168 std::vector<CanonicalLoopInfo *> Result;
7169 Result.reserve(NumLoops * 2);
7170
7171 // The basic block of the surrounding loop that enters the nest generated
7172 // loop.
7173 BasicBlock *Enter = OutermostLoop->getPreheader();
7174
7175 // The basic block of the surrounding loop where the inner code should
7176 // continue.
7177 BasicBlock *Continue = OutermostLoop->getAfter();
7178
7179 // Where the next loop basic block should be inserted.
7180 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7181
7182 auto EmbeddNewLoop =
7183 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7184 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7185 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7186 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7187 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7188 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7189
7190 // Setup the position where the next embedded loop connects to this loop.
7191 Enter = EmbeddedLoop->getBody();
7192 Continue = EmbeddedLoop->getLatch();
7193 OutroInsertBefore = EmbeddedLoop->getLatch();
7194 return EmbeddedLoop;
7195 };
7196
7197 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7198 const Twine &NameBase) {
7199 for (auto P : enumerate(TripCounts)) {
7200 CanonicalLoopInfo *EmbeddedLoop =
7201 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7202 Result.push_back(EmbeddedLoop);
7203 }
7204 };
7205
7206 EmbeddNewLoops(FloorCount, "floor");
7207
7208 // Within the innermost floor loop, emit the code that computes the tile
7209 // sizes.
7210 Builder.SetInsertPoint(Enter->getTerminator());
7211 SmallVector<Value *, 4> TileCounts;
7212 for (int i = 0; i < NumLoops; ++i) {
7213 CanonicalLoopInfo *FloorLoop = Result[i];
7214 Value *TileSize = TileSizes[i];
7215
7216 Value *FloorIsEpilogue =
7217 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7218 Value *TileTripCount =
7219 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7220
7221 TileCounts.push_back(TileTripCount);
7222 }
7223
7224 // Create the tile loops.
7225 EmbeddNewLoops(TileCounts, "tile");
7226
7227 // Insert the inbetween code into the body.
7228 BasicBlock *BodyEnter = Enter;
7229 BasicBlock *BodyEntered = nullptr;
7230 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7231 BasicBlock *EnterBB = P.first;
7232 BasicBlock *ExitBB = P.second;
7233
7234 if (BodyEnter)
7235 redirectTo(BodyEnter, EnterBB, DL);
7236 else
7237 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7238
7239 BodyEnter = nullptr;
7240 BodyEntered = ExitBB;
7241 }
7242
7243 // Append the original loop nest body into the generated loop nest body.
7244 if (BodyEnter)
7245 redirectTo(BodyEnter, InnerEnter, DL);
7246 else
7247 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7249
7250 // Replace the original induction variable with an induction variable computed
7251 // from the tile and floor induction variables.
7252 Builder.restoreIP(Result.back()->getBodyIP());
7253 for (int i = 0; i < NumLoops; ++i) {
7254 CanonicalLoopInfo *FloorLoop = Result[i];
7255 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7256 Value *OrigIndVar = OrigIndVars[i];
7257 Value *Size = TileSizes[i];
7258
7259 Value *Scale =
7260 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7261 Value *Shift =
7262 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7263 OrigIndVar->replaceAllUsesWith(Shift);
7264 }
7265
7266 // Remove unused parts of the original loops.
7267 removeUnusedBlocksFromParent(OldControlBBs);
7268
7269 for (CanonicalLoopInfo *L : Loops)
7270 L->invalidate();
7271
7272#ifndef NDEBUG
7273 for (CanonicalLoopInfo *GenL : Result)
7274 GenL->assertOK();
7275#endif
7276 return Result;
7277}
7278
7279/// Attach metadata \p Properties to the basic block described by \p BB. If the
7280/// basic block already has metadata, the basic block properties are appended.
7282 ArrayRef<Metadata *> Properties) {
7283 // Nothing to do if no property to attach.
7284 if (Properties.empty())
7285 return;
7286
7287 LLVMContext &Ctx = BB->getContext();
7288 SmallVector<Metadata *> NewProperties;
7289 NewProperties.push_back(nullptr);
7290
7291 // If the basic block already has metadata, prepend it to the new metadata.
7292 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7293 if (Existing)
7294 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7295
7296 append_range(NewProperties, Properties);
7297 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7298 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7299
7300 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7301}
7302
7303/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7304/// loop already has metadata, the loop properties are appended.
7306 ArrayRef<Metadata *> Properties) {
7307 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7308
7309 // Attach metadata to the loop's latch
7310 BasicBlock *Latch = Loop->getLatch();
7311 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7312 addBasicBlockMetadata(Latch, Properties);
7313}
7314
7315/// Attach llvm.access.group metadata to the memref instructions of \p Block
7317 LoopInfo &LI) {
7318 for (Instruction &I : *Block) {
7319 if (I.mayReadOrWriteMemory()) {
7320 // TODO: This instruction may already have access group from
7321 // other pragmas e.g. #pragma clang loop vectorize. Append
7322 // so that the existing metadata is not overwritten.
7323 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7324 }
7325 }
7326}
7327
7328CanonicalLoopInfo *
7330 CanonicalLoopInfo *firstLoop = Loops.front();
7331 CanonicalLoopInfo *lastLoop = Loops.back();
7332 Function *F = firstLoop->getPreheader()->getParent();
7333
7334 // Loop control blocks that will become orphaned later
7335 SmallVector<BasicBlock *> oldControlBBs;
7337 Loop->collectControlBlocks(oldControlBBs);
7338
7339 // Collect original trip counts
7340 SmallVector<Value *> origTripCounts;
7341 for (CanonicalLoopInfo *L : Loops) {
7342 assert(L->isValid() && "All input loops must be valid canonical loops");
7343 origTripCounts.push_back(L->getTripCount());
7344 }
7345
7346 Builder.SetCurrentDebugLocation(DL);
7347
7348 // Compute max trip count.
7349 // The fused loop will be from 0 to max(origTripCounts)
7350 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7351 F, firstLoop->getHeader());
7352 Builder.SetInsertPoint(TCBlock);
7353 Value *fusedTripCount = nullptr;
7354 for (CanonicalLoopInfo *L : Loops) {
7355 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7356 Value *origTripCount = L->getTripCount();
7357 if (!fusedTripCount) {
7358 fusedTripCount = origTripCount;
7359 continue;
7360 }
7361 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7362 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7363 ".omp.fuse.tc");
7364 }
7365
7366 // Generate new loop
7367 CanonicalLoopInfo *fused =
7368 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7369 lastLoop->getLatch(), "fused");
7370
7371 // Replace original loops with the fused loop
7372 // Preheader and After are not considered inside the CLI.
7373 // These are used to compute the individual TCs of the loops
7374 // so they have to be put before the resulting fused loop.
7375 // Moving them up for readability.
7376 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7377 Loops[i]->getPreheader()->moveBefore(TCBlock);
7378 Loops[i]->getAfter()->moveBefore(TCBlock);
7379 }
7380 lastLoop->getPreheader()->moveBefore(TCBlock);
7381
7382 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7383 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7384 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7385 }
7386 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7387 redirectTo(TCBlock, fused->getPreheader(), DL);
7388 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7389
7390 // Build the fused body
7391 // Create new Blocks with conditions that jump to the original loop bodies
7393 SmallVector<Value *> condValues;
7394 for (size_t i = 0; i < Loops.size(); ++i) {
7395 BasicBlock *condBlock = BasicBlock::Create(
7396 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7397 Builder.SetInsertPoint(condBlock);
7398 Value *condValue =
7399 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7400 condBBs.push_back(condBlock);
7401 condValues.push_back(condValue);
7402 }
7403 // Join the condition blocks with the bodies of the original loops
7404 redirectTo(fused->getBody(), condBBs[0], DL);
7405 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7406 Builder.SetInsertPoint(condBBs[i]);
7407 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7408 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7409 // Replace the IV with the fused IV
7410 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7411 }
7412 // Last body jumps to the created end body block
7413 Builder.SetInsertPoint(condBBs.back());
7414 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7415 fused->getLatch());
7416 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7417 // Replace the IV with the fused IV
7418 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7419
7420 // The loop latch must have only one predecessor. Currently it is branched to
7421 // from both the last condition block and the last loop body
7422 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7423 "omp.fused.pre_latch");
7424
7425 // Remove unused parts
7426 removeUnusedBlocksFromParent(oldControlBBs);
7427
7428 // Invalidate old CLIs
7429 for (CanonicalLoopInfo *L : Loops)
7430 L->invalidate();
7431
7432#ifndef NDEBUG
7433 fused->assertOK();
7434#endif
7435 return fused;
7436}
7437
7439 LLVMContext &Ctx = Builder.getContext();
7441 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7442 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7443}
7444
7446 LLVMContext &Ctx = Builder.getContext();
7448 Loop, {
7449 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7450 });
7451}
7452
7453void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7454 Value *IfCond, ValueToValueMapTy &VMap,
7455 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7456 const Twine &NamePrefix) {
7457 Function *F = CanonicalLoop->getFunction();
7458
7459 // We can't do
7460 // if (cond) {
7461 // simd_loop;
7462 // } else {
7463 // non_simd_loop;
7464 // }
7465 // because then the CanonicalLoopInfo would only point to one of the loops:
7466 // leading to other constructs operating on the same loop to malfunction.
7467 // Instead generate
7468 // while (...) {
7469 // if (cond) {
7470 // simd_body;
7471 // } else {
7472 // not_simd_body;
7473 // }
7474 // }
7475 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7476 // body at -O3
7477
7478 // Define where if branch should be inserted
7479 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7480
7481 // Create additional blocks for the if statement
7482 BasicBlock *Cond = SplitBeforeIt->getParent();
7483 llvm::LLVMContext &C = Cond->getContext();
7485 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7487 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7488
7489 // Create if condition branch.
7490 Builder.SetInsertPoint(SplitBeforeIt);
7491 Instruction *BrInstr =
7492 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7493 InsertPointTy IP{BrInstr->getParent(), ++BrInstr->getIterator()};
7494 // Then block contains branch to omp loop body which needs to be vectorized
7495 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7496 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7497
7498 Builder.SetInsertPoint(ElseBlock);
7499
7500 // Clone loop for the else branch
7502
7503 SmallVector<BasicBlock *, 8> ExistingBlocks;
7504 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7505 ExistingBlocks.push_back(ThenBlock);
7506 ExistingBlocks.append(L->block_begin(), L->block_end());
7507 // Cond is the block that has the if clause condition
7508 // LoopCond is omp_loop.cond
7509 // LoopHeader is omp_loop.header
7510 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7511 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7512 assert(LoopCond && LoopHeader && "Invalid loop structure");
7513 for (BasicBlock *Block : ExistingBlocks) {
7514 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7515 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7516 continue;
7517 }
7518 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7519
7520 // fix name not to be omp.if.then
7521 if (Block == ThenBlock)
7522 NewBB->setName(NamePrefix + ".if.else");
7523
7524 NewBB->moveBefore(CanonicalLoop->getExit());
7525 VMap[Block] = NewBB;
7526 NewBlocks.push_back(NewBB);
7527 }
7528 remapInstructionsInBlocks(NewBlocks, VMap);
7529 Builder.CreateBr(NewBlocks.front());
7530
7531 // The loop latch must have only one predecessor. Currently it is branched to
7532 // from both the 'then' and 'else' branches.
7533 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7534 NamePrefix + ".pre_latch");
7535
7536 // Ensure that the then block is added to the loop so we add the attributes in
7537 // the next step
7538 L->addBasicBlockToLoop(ThenBlock, LI);
7539}
7540
7541unsigned
7543 const StringMap<bool> &Features) {
7544 if (TargetTriple.isX86()) {
7545 if (Features.lookup("avx512f"))
7546 return 512;
7547 else if (Features.lookup("avx"))
7548 return 256;
7549 return 128;
7550 }
7551 if (TargetTriple.isPPC())
7552 return 128;
7553 if (TargetTriple.isWasm())
7554 return 128;
7555 return 0;
7556}
7557
7559 MapVector<Value *, Value *> AlignedVars,
7560 Value *IfCond, OrderKind Order,
7561 ConstantInt *Simdlen, ConstantInt *Safelen) {
7562 LLVMContext &Ctx = Builder.getContext();
7563
7564 Function *F = CanonicalLoop->getFunction();
7565
7566 // Blocks must have terminators.
7567 // FIXME: Don't run analyses on incomplete/invalid IR.
7569 for (BasicBlock &BB : *F)
7570 if (!BB.hasTerminator())
7571 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7572
7573 // TODO: We should not rely on pass manager. Currently we use pass manager
7574 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7575 // object. We should have a method which returns all blocks between
7576 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7578 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7579 FAM.registerPass([]() { return LoopAnalysis(); });
7580 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7581
7582 LoopAnalysis LIA;
7583 LoopInfo &&LI = LIA.run(*F, FAM);
7584
7585 for (Instruction *I : UIs)
7586 I->eraseFromParent();
7587
7588 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7589 if (AlignedVars.size()) {
7590 InsertPointTy IP = Builder.saveIP();
7591 for (auto &AlignedItem : AlignedVars) {
7592 Value *AlignedPtr = AlignedItem.first;
7593 Value *Alignment = AlignedItem.second;
7594 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7595 Builder.SetInsertPoint(loadInst->getNextNode());
7596 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7597 Alignment);
7598 }
7599 Builder.restoreIP(IP);
7600 }
7601
7602 if (IfCond) {
7603 ValueToValueMapTy VMap;
7604 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7605 }
7606
7608
7609 // Get the basic blocks from the loop in which memref instructions
7610 // can be found.
7611 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7612 // preferably without running any passes.
7613 for (BasicBlock *Block : L->getBlocks()) {
7614 if (Block == CanonicalLoop->getCond() ||
7615 Block == CanonicalLoop->getHeader())
7616 continue;
7617 Reachable.insert(Block);
7618 }
7619
7620 SmallVector<Metadata *> LoopMDList;
7621
7622 // In presence of finite 'safelen', it may be unsafe to mark all
7623 // the memory instructions parallel, because loop-carried
7624 // dependences of 'safelen' iterations are possible.
7625 // If clause order(concurrent) is specified then the memory instructions
7626 // are marked parallel even if 'safelen' is finite.
7627 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7628 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7629
7630 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7631 // versions so we can't add the loop attributes in that case.
7632 if (IfCond) {
7633 // we can still add llvm.loop.parallel_access
7634 addLoopMetadata(CanonicalLoop, LoopMDList);
7635 return;
7636 }
7637
7638 // Use the above access group metadata to create loop level
7639 // metadata, which should be distinct for each loop.
7640 LoopMDList.push_back(
7641 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7642
7643 if (Simdlen || Safelen) {
7644 // If both simdlen and safelen clauses are specified, the value of the
7645 // simdlen parameter must be less than or equal to the value of the safelen
7646 // parameter. Therefore, use safelen only in the absence of simdlen.
7647 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7648 LoopMDList.push_back(
7649 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7650 ConstantAsMetadata::get(VectorizeWidth)}));
7651 }
7652
7653 addLoopMetadata(CanonicalLoop, LoopMDList);
7654}
7655
7656/// Create the TargetMachine object to query the backend for optimization
7657/// preferences.
7658///
7659/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7660/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7661/// needed for the LLVM pass pipline. We use some default options to avoid
7662/// having to pass too many settings from the frontend that probably do not
7663/// matter.
7664///
7665/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7666/// method. If we are going to use TargetMachine for more purposes, especially
7667/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7668/// might become be worth requiring front-ends to pass on their TargetMachine,
7669/// or at least cache it between methods. Note that while fontends such as Clang
7670/// have just a single main TargetMachine per translation unit, "target-cpu" and
7671/// "target-features" that determine the TargetMachine are per-function and can
7672/// be overrided using __attribute__((target("OPTIONS"))).
7673static std::unique_ptr<TargetMachine>
7675 Module *M = F->getParent();
7676
7677 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7678 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7679 const llvm::Triple &Triple = M->getTargetTriple();
7680
7681 std::string Error;
7683 if (!TheTarget)
7684 return {};
7685
7687 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7688 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7689 /*CodeModel=*/std::nullopt, OptLevel));
7690}
7691
7692/// Heuristically determine the best-performant unroll factor for \p CLI. This
7693/// depends on the target processor. We are re-using the same heuristics as the
7694/// LoopUnrollPass.
7696 Function *F = CLI->getFunction();
7697
7698 // Assume the user requests the most aggressive unrolling, even if the rest of
7699 // the code is optimized using a lower setting.
7701 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7702
7703 // Blocks must have terminators.
7704 // FIXME: Don't run analyses on incomplete/invalid IR.
7706 for (BasicBlock &BB : *F)
7707 if (!BB.hasTerminator())
7708 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7709
7711 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7712 FAM.registerPass([]() { return AssumptionAnalysis(); });
7713 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7714 FAM.registerPass([]() { return LoopAnalysis(); });
7715 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7716 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7717 TargetIRAnalysis TIRA;
7718 if (TM)
7719 TIRA = TargetIRAnalysis(
7720 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7721 FAM.registerPass([&]() { return TIRA; });
7722
7723 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7725 ScalarEvolution &&SE = SEA.run(*F, FAM);
7727 DominatorTree &&DT = DTA.run(*F, FAM);
7728 LoopAnalysis LIA;
7729 LoopInfo &&LI = LIA.run(*F, FAM);
7731 AssumptionCache &&AC = ACT.run(*F, FAM);
7733
7734 for (Instruction *I : UIs)
7735 I->eraseFromParent();
7736
7737 Loop *L = LI.getLoopFor(CLI->getHeader());
7738 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7739
7741 L, SE, TTI,
7742 /*BlockFrequencyInfo=*/nullptr,
7743 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7744 /*UserThreshold=*/std::nullopt,
7745 /*UserAllowPartial=*/true,
7746 /*UserAllowRuntime=*/true,
7747 /*UserUpperBound=*/std::nullopt,
7748 /*UserFullUnrollMaxCount=*/std::nullopt);
7749
7750 UP.Force = true;
7751
7752 // Account for additional optimizations taking place before the LoopUnrollPass
7753 // would unroll the loop.
7756
7757 // Use normal unroll factors even if the rest of the code is optimized for
7758 // size.
7761
7762 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7763 << " Threshold=" << UP.Threshold << "\n"
7764 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7765 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7766 << " PartialOptSizeThreshold="
7767 << UP.PartialOptSizeThreshold << "\n");
7768
7769 // Disable peeling.
7772 /*UserAllowPeeling=*/false,
7773 /*UserAllowProfileBasedPeeling=*/false,
7774 /*UnrollingSpecficValues=*/false);
7775
7777 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7778
7779 // Assume that reads and writes to stack variables can be eliminated by
7780 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7781 // size.
7782 for (BasicBlock *BB : L->blocks()) {
7783 for (Instruction &I : *BB) {
7784 Value *Ptr;
7785 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7786 Ptr = Load->getPointerOperand();
7787 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7788 Ptr = Store->getPointerOperand();
7789 } else
7790 continue;
7791
7792 Ptr = Ptr->stripPointerCasts();
7793
7794 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7795 if (Alloca->getParent() == &F->getEntryBlock())
7796 EphValues.insert(&I);
7797 }
7798 }
7799 }
7800
7801 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7802
7803 // Loop is not unrollable if the loop contains certain instructions.
7804 if (!UCE.canUnroll()) {
7805 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7806 return 1;
7807 }
7808
7809 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7810 << "\n");
7811
7812 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7813 // be able to use it.
7814 int TripCount = 0;
7815 int MaxTripCount = 0;
7816 bool MaxOrZero = false;
7817 unsigned TripMultiple = 0;
7818
7819 unsigned Factor =
7820 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7821 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7822 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7823
7824 // This function returns 1 to signal to not unroll a loop.
7825 if (Factor == 0)
7826 return 1;
7827 return Factor;
7828}
7829
7831 int32_t Factor,
7832 CanonicalLoopInfo **UnrolledCLI) {
7833 assert(Factor >= 0 && "Unroll factor must not be negative");
7834
7835 Function *F = Loop->getFunction();
7836 LLVMContext &Ctx = F->getContext();
7837
7838 // If the unrolled loop is not used for another loop-associated directive, it
7839 // is sufficient to add metadata for the LoopUnrollPass.
7840 if (!UnrolledCLI) {
7841 SmallVector<Metadata *, 2> LoopMetadata;
7842 LoopMetadata.push_back(
7843 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7844
7845 if (Factor >= 1) {
7847 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7848 LoopMetadata.push_back(MDNode::get(
7849 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7850 }
7851
7852 addLoopMetadata(Loop, LoopMetadata);
7853 return;
7854 }
7855
7856 // Heuristically determine the unroll factor.
7857 if (Factor == 0)
7859
7860 // No change required with unroll factor 1.
7861 if (Factor == 1) {
7862 *UnrolledCLI = Loop;
7863 return;
7864 }
7865
7866 assert(Factor >= 2 &&
7867 "unrolling only makes sense with a factor of 2 or larger");
7868
7869 Type *IndVarTy = Loop->getIndVarType();
7870
7871 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7872 // unroll the inner loop.
7873 Value *FactorVal =
7874 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7875 /*isSigned=*/false));
7876 std::vector<CanonicalLoopInfo *> LoopNest =
7877 tileLoops(DL, {Loop}, {FactorVal});
7878 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7879 *UnrolledCLI = LoopNest[0];
7880 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7881
7882 // LoopUnrollPass can only fully unroll loops with constant trip count.
7883 // Unroll by the unroll factor with a fallback epilog for the remainder
7884 // iterations if necessary.
7886 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7888 InnerLoop,
7889 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7891 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
7892
7893#ifndef NDEBUG
7894 (*UnrolledCLI)->assertOK();
7895#endif
7896}
7897
7900 llvm::Value *BufSize, llvm::Value *CpyBuf,
7901 llvm::Value *CpyFn, llvm::Value *DidIt) {
7902 if (!updateToLocation(Loc))
7903 return Loc.IP;
7904
7905 uint32_t SrcLocStrSize;
7906 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7907 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7908 Value *ThreadId = getOrCreateThreadID(Ident);
7909
7910 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
7911
7912 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
7913
7914 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
7915 createRuntimeFunctionCall(Fn, Args);
7916
7917 return Builder.saveIP();
7918}
7919
7921 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7922 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
7924
7925 if (!updateToLocation(Loc))
7926 return Loc.IP;
7927
7928 // If needed allocate and initialize `DidIt` with 0.
7929 // DidIt: flag variable: 1=single thread; 0=not single thread.
7930 llvm::Value *DidIt = nullptr;
7931 if (!CPVars.empty()) {
7932 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
7933 Builder.CreateStore(Builder.getInt32(0), DidIt);
7934 }
7935
7936 Directive OMPD = Directive::OMPD_single;
7937 uint32_t SrcLocStrSize;
7938 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7939 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7940 Value *ThreadId = getOrCreateThreadID(Ident);
7941 Value *Args[] = {Ident, ThreadId};
7942
7943 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
7944 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
7945
7946 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
7947 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
7948
7949 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
7950 if (Error Err = FiniCB(IP))
7951 return Err;
7952
7953 // The thread that executes the single region must set `DidIt` to 1.
7954 // This is used by __kmpc_copyprivate, to know if the caller is the
7955 // single thread or not.
7956 if (DidIt)
7957 Builder.CreateStore(Builder.getInt32(1), DidIt);
7958
7959 return Error::success();
7960 };
7961
7962 // generates the following:
7963 // if (__kmpc_single()) {
7964 // .... single region ...
7965 // __kmpc_end_single
7966 // }
7967 // __kmpc_copyprivate
7968 // __kmpc_barrier
7969
7970 InsertPointOrErrorTy AfterIP =
7971 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
7972 /*Conditional*/ true,
7973 /*hasFinalize*/ true);
7974 if (!AfterIP)
7975 return AfterIP.takeError();
7976
7977 if (DidIt) {
7978 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
7979 // NOTE BufSize is currently unused, so just pass 0.
7981 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
7982 CPFuncs[I], DidIt);
7983 // NOTE __kmpc_copyprivate already inserts a barrier
7984 } else if (!IsNowait) {
7985 InsertPointOrErrorTy AfterIP =
7987 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
7988 /* CheckCancelFlag */ false);
7989 if (!AfterIP)
7990 return AfterIP.takeError();
7991 }
7992 return Builder.saveIP();
7993}
7994
7997 BodyGenCallbackTy BodyGenCB,
7998 FinalizeCallbackTy FiniCB, bool IsNowait) {
7999
8000 if (!updateToLocation(Loc))
8001 return Loc.IP;
8002
8003 // All threads execute the scope body — no conditional entry.
8004 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8005 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8006 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8007 /*IsCancellable=*/false);
8008 if (!AfterIP)
8009 return AfterIP.takeError();
8010
8011 Builder.restoreIP(*AfterIP);
8012 if (!IsNowait) {
8013 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8014 omp::Directive::OMPD_unknown,
8015 /*ForceSimpleCall=*/false,
8016 /*CheckCancelFlag=*/false);
8017 if (!AfterIP)
8018 return AfterIP.takeError();
8019 }
8020 return Builder.saveIP();
8021}
8022
8024 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8025 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8026
8027 if (!updateToLocation(Loc))
8028 return Loc.IP;
8029
8030 Directive OMPD = Directive::OMPD_critical;
8031 uint32_t SrcLocStrSize;
8032 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8033 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8034 Value *ThreadId = getOrCreateThreadID(Ident);
8035 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8036 Value *Args[] = {Ident, ThreadId, LockVar};
8037
8038 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8039 Function *RTFn = nullptr;
8040 if (HintInst) {
8041 // Add Hint to entry Args and create call
8042 EnterArgs.push_back(HintInst);
8043 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8044 } else {
8045 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8046 }
8047 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8048
8049 Function *ExitRTLFn =
8050 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8051 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8052
8053 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8054 /*Conditional*/ false, /*hasFinalize*/ true);
8055}
8056
8059 InsertPointTy AllocaIP, unsigned NumLoops,
8060 ArrayRef<llvm::Value *> StoreValues,
8061 const Twine &Name, bool IsDependSource) {
8062 assert(
8063 llvm::all_of(StoreValues,
8064 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8065 "OpenMP runtime requires depend vec with i64 type");
8066
8067 if (!updateToLocation(Loc))
8068 return Loc.IP;
8069
8070 // Allocate space for vector and generate alloc instruction.
8071 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8072 Builder.restoreIP(AllocaIP);
8073 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8074 ArgsBase->setAlignment(Align(8));
8076
8077 // Store the index value with offset in depend vector.
8078 for (unsigned I = 0; I < NumLoops; ++I) {
8079 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8080 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8081 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8082 STInst->setAlignment(Align(8));
8083 }
8084
8085 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8086 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8087
8088 uint32_t SrcLocStrSize;
8089 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8090 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8091 Value *ThreadId = getOrCreateThreadID(Ident);
8092 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8093
8094 Function *RTLFn = nullptr;
8095 if (IsDependSource)
8096 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8097 else
8098 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8099 createRuntimeFunctionCall(RTLFn, Args);
8100
8101 return Builder.saveIP();
8102}
8103
8105 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8106 FinalizeCallbackTy FiniCB, bool IsThreads) {
8107 if (!updateToLocation(Loc))
8108 return Loc.IP;
8109
8110 Directive OMPD = Directive::OMPD_ordered;
8111 Instruction *EntryCall = nullptr;
8112 Instruction *ExitCall = nullptr;
8113
8114 if (IsThreads) {
8115 uint32_t SrcLocStrSize;
8116 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8117 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8118 Value *ThreadId = getOrCreateThreadID(Ident);
8119 Value *Args[] = {Ident, ThreadId};
8120
8121 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8122 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8123
8124 Function *ExitRTLFn =
8125 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8126 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8127 }
8128
8129 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8130 /*Conditional*/ false, /*hasFinalize*/ true);
8131}
8132
8133OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8134 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8135 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8136 bool HasFinalize, bool IsCancellable) {
8137
8138 if (HasFinalize)
8139 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8140
8141 // Create inlined region's entry and body blocks, in preparation
8142 // for conditional creation
8143 BasicBlock *EntryBB = Builder.GetInsertBlock();
8144 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8146 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8147 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8148 BasicBlock *FiniBB =
8149 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8150
8151 Builder.SetInsertPoint(EntryBB->getTerminator());
8152 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8153
8154 // generate body
8155 if (Error Err =
8156 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8157 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8158 return Err;
8159
8160 // emit exit call and do any needed finalization.
8161 auto FinIP = InsertPointTy(FiniBB, FiniBB->getFirstInsertionPt());
8162 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8163 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8164 "Unexpected control flow graph state!!");
8165 InsertPointOrErrorTy AfterIP =
8166 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8167 if (!AfterIP)
8168 return AfterIP.takeError();
8169
8170 // If we are skipping the region of a non conditional, remove the exit
8171 // block, and clear the builder's insertion point.
8172 assert(SplitPos->getParent() == ExitBB &&
8173 "Unexpected Insertion point location!");
8174 auto merged = MergeBlockIntoPredecessor(ExitBB);
8175 BasicBlock *ExitPredBB = SplitPos->getParent();
8176 auto InsertBB = merged ? ExitPredBB : ExitBB;
8178 SplitPos->eraseFromParent();
8179 Builder.SetInsertPoint(InsertBB);
8180
8181 return Builder.saveIP();
8182}
8183
8184OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8185 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8186 // if nothing to do, Return current insertion point.
8187 if (!Conditional || !EntryCall)
8188 return Builder.saveIP();
8189
8190 BasicBlock *EntryBB = Builder.GetInsertBlock();
8191 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8192 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8193 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8194
8195 // Emit thenBB and set the Builder's insertion point there for
8196 // body generation next. Place the block after the current block.
8197 Function *CurFn = EntryBB->getParent();
8198 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8199
8200 // Move Entry branch to end of ThenBB, and replace with conditional
8201 // branch (If-stmt)
8202 Instruction *EntryBBTI = EntryBB->getTerminator();
8203 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8204 EntryBBTI->removeFromParent();
8205 Builder.SetInsertPoint(UI);
8206 Builder.Insert(EntryBBTI);
8207 UI->eraseFromParent();
8208 Builder.SetInsertPoint(ThenBB->getTerminator());
8209
8210 // return an insertion point to ExitBB.
8211 return IRBuilder<>::InsertPoint(ExitBB, ExitBB->getFirstInsertionPt());
8212}
8213
8214OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8215 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8216 bool HasFinalize) {
8217
8218 Builder.restoreIP(FinIP);
8219
8220 // If there is finalization to do, emit it before the exit call
8221 if (HasFinalize) {
8222 assert(!FinalizationStack.empty() &&
8223 "Unexpected finalization stack state!");
8224
8225 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8226 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8227
8228 if (Error Err = Fi.mergeFiniBB(Builder, FinIP.getBlock()))
8229 return std::move(Err);
8230
8231 // Exit condition: insertion point is before the terminator of the new Fini
8232 // block
8233 Builder.SetInsertPoint(FinIP.getBlock()->getTerminator());
8234 }
8235
8236 if (!ExitCall)
8237 return Builder.saveIP();
8238
8239 // place the Exitcall as last instruction before Finalization block terminator
8240 ExitCall->removeFromParent();
8241 Builder.Insert(ExitCall);
8242
8243 return IRBuilder<>::InsertPoint(ExitCall->getParent(),
8244 ExitCall->getIterator());
8245}
8246
8248 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8249 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8250 if (!IP.isSet())
8251 return IP;
8252
8254
8255 // creates the following CFG structure
8256 // OMP_Entry : (MasterAddr != PrivateAddr)?
8257 // F T
8258 // | \
8259 // | copin.not.master
8260 // | /
8261 // v /
8262 // copyin.not.master.end
8263 // |
8264 // v
8265 // OMP.Entry.Next
8266
8267 BasicBlock *OMP_Entry = IP.getBlock();
8268 Function *CurFn = OMP_Entry->getParent();
8269 BasicBlock *CopyBegin =
8270 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8271 BasicBlock *CopyEnd = nullptr;
8272
8273 // If entry block is terminated, split to preserve the branch to following
8274 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8276 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8277 "copyin.not.master.end");
8278 OMP_Entry->getTerminator()->eraseFromParent();
8279 } else {
8280 CopyEnd =
8281 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8282 }
8283
8284 Builder.SetInsertPoint(OMP_Entry);
8285 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8286 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8287 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8288 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8289
8290 Builder.SetInsertPoint(CopyBegin);
8291 if (BranchtoEnd)
8292 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8293
8294 return Builder.saveIP();
8295}
8296
8298 Value *Size, Value *Allocator,
8299 std::string Name) {
8301 if (!updateToLocation(Loc))
8302 return nullptr;
8303
8304 uint32_t SrcLocStrSize;
8305 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8306 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8307 Value *ThreadId = getOrCreateThreadID(Ident);
8308 Value *Args[] = {ThreadId, Size, Allocator};
8309
8310 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8311
8312 return createRuntimeFunctionCall(Fn, Args, Name);
8313}
8314
8316 Value *Align, Value *Size,
8317 Value *Allocator,
8318 std::string Name) {
8320 if (!updateToLocation(Loc))
8321 return nullptr;
8322
8323 uint32_t SrcLocStrSize;
8324 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8325 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8326 Value *ThreadId = getOrCreateThreadID(Ident);
8327 Value *Args[] = {ThreadId, Align, Size, Allocator};
8328
8329 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8330
8331 return Builder.CreateCall(Fn, Args, Name);
8332}
8333
8335 Value *Addr, Value *Allocator,
8336 std::string Name) {
8338 if (!updateToLocation(Loc))
8339 return nullptr;
8340
8341 uint32_t SrcLocStrSize;
8342 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8343 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8344 Value *ThreadId = getOrCreateThreadID(Ident);
8345 Value *Args[] = {ThreadId, Addr, Allocator};
8346 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8347 return createRuntimeFunctionCall(Fn, Args, Name);
8348}
8349
8351 Value *Size,
8352 const Twine &Name) {
8355
8356 Value *Args[] = {Size};
8357 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8358 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8360 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8361 return Call;
8362}
8363
8365 Type *VarType,
8366 const Twine &Name) {
8367 return createOMPAllocShared(
8368 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8369}
8370
8372 Value *Addr, Value *Size,
8373 const Twine &Name) {
8376
8377 Value *Args[] = {Addr, Size};
8378 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8379 return Builder.CreateCall(Fn, Args, Name);
8380}
8381
8383 Value *Addr, Type *VarType,
8384 const Twine &Name) {
8385 return createOMPFreeShared(
8386 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8387 Name);
8388}
8389
8391 const LocationDescription &Loc, Value *InteropVar,
8392 omp::OMPInteropType InteropType, Value *Device, Value *NumDependences,
8393 Value *DependenceAddress, bool HaveNowaitClause) {
8396
8397 uint32_t SrcLocStrSize;
8398 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8399 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8400 Value *ThreadId = getOrCreateThreadID(Ident);
8401 if (Device == nullptr)
8402 Device = Constant::getAllOnesValue(Int32);
8403 else if (Device->getType() != Int32)
8404 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8405 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8406 if (NumDependences == nullptr) {
8407 NumDependences = ConstantInt::get(Int32, 0);
8408 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8409 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8410 }
8411 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8412 Value *Args[] = {
8413 Ident, ThreadId, InteropVar, InteropTypeVal,
8414 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8415
8416 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8417
8418 return createRuntimeFunctionCall(Fn, Args);
8419}
8420
8422 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8423 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8426
8427 uint32_t SrcLocStrSize;
8428 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8429 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8430 Value *ThreadId = getOrCreateThreadID(Ident);
8431 if (Device == nullptr)
8432 Device = Constant::getAllOnesValue(Int32);
8433 else if (Device->getType() != Int32)
8434 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8435 if (NumDependences == nullptr) {
8436 NumDependences = ConstantInt::get(Int32, 0);
8437 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8438 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8439 }
8440 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8441 Value *Args[] = {
8442 Ident, ThreadId, InteropVar, Device,
8443 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8444
8445 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8446
8447 return createRuntimeFunctionCall(Fn, Args);
8448}
8449
8451 Value *InteropVar, Value *Device,
8452 Value *NumDependences,
8453 Value *DependenceAddress,
8454 bool HaveNowaitClause) {
8457 uint32_t SrcLocStrSize;
8458 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8459 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8460 Value *ThreadId = getOrCreateThreadID(Ident);
8461 if (Device == nullptr)
8462 Device = Constant::getAllOnesValue(Int32);
8463 else if (Device->getType() != Int32)
8464 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8465 if (NumDependences == nullptr) {
8466 NumDependences = ConstantInt::get(Int32, 0);
8467 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8468 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8469 }
8470 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8471 Value *Args[] = {
8472 Ident, ThreadId, InteropVar, Device,
8473 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8474
8475 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8476
8477 return createRuntimeFunctionCall(Fn, Args);
8478}
8479
8482 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8485
8486 uint32_t SrcLocStrSize;
8487 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8488 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8489 Value *ThreadId = getOrCreateThreadID(Ident);
8490 Constant *ThreadPrivateCache =
8491 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8492 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8493
8494 Function *Fn =
8495 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8496
8497 return createRuntimeFunctionCall(Fn, Args);
8498}
8499
8501 const LocationDescription &Loc,
8503 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8504 "expected num_threads and num_teams to be specified");
8505
8506 if (!updateToLocation(Loc))
8507 return Loc.IP;
8508
8509 uint32_t SrcLocStrSize;
8510 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8511 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8512 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8513 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8514 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8515 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8516 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8517 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8518
8519 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8520 Function *Kernel = DebugKernelWrapper;
8521
8522 // We need to strip the debug prefix to get the correct kernel name.
8523 StringRef KernelName = Kernel->getName();
8524 const std::string DebugPrefix = "_debug__";
8525 if (KernelName.ends_with(DebugPrefix)) {
8526 KernelName = KernelName.drop_back(DebugPrefix.length());
8527 Kernel = M.getFunction(KernelName);
8528 assert(Kernel && "Expected the real kernel to exist");
8529 }
8530
8531 // Manifest the launch configuration in the metadata matching the kernel
8532 // environment.
8533 if (Attrs.MinTeams > 1 || Attrs.MaxTeams.front() > 0)
8534 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams, Attrs.MaxTeams.front());
8535
8536 // If MaxThreads is not set and needs adjustment, select the maximum between
8537 // the default workgroup size and the MinThreads value.
8538 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8539 if (MaxThreadsVal < 0 && UseDefaultMaxThreads) {
8540 if (hasGridValue(T)) {
8541 MaxThreadsVal =
8542 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8543 Attrs.MinThreads);
8544 } else {
8545 MaxThreadsVal = Attrs.MinThreads;
8546 }
8547 }
8548
8549 if (MaxThreadsVal > 0)
8550 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads, MaxThreadsVal);
8551
8552 Constant *MinThreads = ConstantInt::getSigned(Int32, Attrs.MinThreads);
8553 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8554 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams);
8555 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8556 Constant *ReductionDataSize =
8557 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8558
8560 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8561 const DataLayout &DL = Fn->getDataLayout();
8562
8563 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8564 Constant *DynamicEnvironmentInitializer =
8565 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8566 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8567 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8568 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8569 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8570 DL.getDefaultGlobalsAddressSpace());
8571 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8572
8573 Constant *DynamicEnvironment =
8574 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8575 ? DynamicEnvironmentGV
8576 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8577 DynamicEnvironmentPtr);
8578
8579 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8580 ConfigurationEnvironment, {
8581 UseGenericStateMachineVal,
8582 MayUseNestedParallelismVal,
8583 IsSPMDVal,
8584 MinThreads,
8585 MaxThreads,
8586 MinTeams,
8587 MaxTeams,
8588 ReductionDataSize,
8589 });
8590 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8591 KernelEnvironment, {
8592 ConfigurationEnvironmentInitializer,
8593 Ident,
8594 DynamicEnvironment,
8595 });
8596 std::string KernelEnvironmentName =
8597 (KernelName + "_kernel_environment").str();
8598 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8599 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8600 KernelEnvironmentInitializer, KernelEnvironmentName,
8601 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8602 DL.getDefaultGlobalsAddressSpace());
8603 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8604
8605 Constant *KernelEnvironment =
8606 KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8607 ? KernelEnvironmentGV
8608 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8609 KernelEnvironmentPtr);
8610 Value *KernelLaunchEnvironment =
8611 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8612 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8613 KernelLaunchEnvironment =
8614 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8615 ? KernelLaunchEnvironment
8616 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8617 KernelLaunchEnvParamTy);
8618 CallInst *ThreadKind = createRuntimeFunctionCall(
8619 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8620
8621 Value *ExecUserCode = Builder.CreateICmpEQ(
8622 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8623 "exec_user_code");
8624
8625 // ThreadKind = __kmpc_target_init(...)
8626 // if (ThreadKind == -1)
8627 // user_code
8628 // else
8629 // return;
8630
8631 auto *UI = Builder.CreateUnreachable();
8632 BasicBlock *CheckBB = UI->getParent();
8633 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8634
8635 BasicBlock *WorkerExitBB = BasicBlock::Create(
8636 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8637 Builder.SetInsertPoint(WorkerExitBB);
8638 Builder.CreateRetVoid();
8639
8640 auto *CheckBBTI = CheckBB->getTerminator();
8641 Builder.SetInsertPoint(CheckBBTI);
8642 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8643
8644 CheckBBTI->eraseFromParent();
8645 UI->eraseFromParent();
8646
8647 // Continue in the "user_code" block, see diagram above and in
8648 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8649 return InsertPointTy(UserCodeEntryBB, UserCodeEntryBB->getFirstInsertionPt());
8650}
8651
8653 int32_t TeamsReductionDataSize) {
8654 if (!updateToLocation(Loc))
8655 return;
8656
8658 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8659
8661
8662 if (!TeamsReductionDataSize)
8663 return;
8664
8665 Function *Kernel = Builder.GetInsertBlock()->getParent();
8666 // We need to strip the debug prefix to get the correct kernel name.
8667 StringRef KernelName = Kernel->getName();
8668 const std::string DebugPrefix = "_debug__";
8669 if (KernelName.ends_with(DebugPrefix))
8670 KernelName = KernelName.drop_back(DebugPrefix.length());
8671 auto *KernelEnvironmentGV =
8672 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8673 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8674 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8675 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8676 KernelEnvironmentInitializer,
8677 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8678 KernelEnvironmentGV->setInitializer(NewInitializer);
8679}
8680
8681static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8682 bool Min) {
8683 if (Kernel.hasFnAttribute(Name)) {
8684 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8685 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8686 }
8687 Kernel.addFnAttr(Name, llvm::utostr(Value));
8688}
8689
8690std::pair<int32_t, int32_t>
8692 int32_t ThreadLimit =
8693 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8694
8695 if (T.isAMDGPU()) {
8696 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8697 if (!Attr.isValid() || !Attr.isStringAttribute())
8698 return {0, ThreadLimit};
8699 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8700 int32_t LB, UB;
8701 if (!llvm::to_integer(UBStr, UB, 10))
8702 return {0, ThreadLimit};
8703 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8704 if (!llvm::to_integer(LBStr, LB, 10))
8705 return {0, UB};
8706 return {LB, UB};
8707 }
8708
8709 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8710 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8711 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8712 }
8713 return {0, ThreadLimit};
8714}
8715
8717 Function &Kernel, int32_t LB,
8718 int32_t UB) {
8719 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8720
8721 if (T.isAMDGPU()) {
8722 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8723 llvm::utostr(LB) + "," + llvm::utostr(UB));
8724 return;
8725 }
8726
8728}
8729
8730std::pair<int32_t, int32_t>
8732 // TODO: Read from backend annotations if available.
8733 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8734}
8735
8737 int32_t LB, int32_t UB) {
8738 if (UB > 0) {
8739 if (T.isNVPTX())
8741 if (T.isAMDGPU())
8742 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8743 }
8744
8745 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8746}
8747
8748void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8749 Function *OutlinedFn) {
8750 if (Config.isTargetDevice()) {
8752 // TODO: Determine if DSO local can be set to true.
8753 OutlinedFn->setDSOLocal(false);
8755 if (T.isAMDGCN())
8757 else if (T.isNVPTX())
8759 else if (T.isSPIRV())
8761 }
8762}
8763
8764Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8765 StringRef EntryFnIDName) {
8766 if (Config.isTargetDevice()) {
8767 assert(OutlinedFn && "The outlined function must exist if embedded");
8768 return OutlinedFn;
8769 }
8770
8771 return new GlobalVariable(
8772 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8773 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8774}
8775
8776Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8777 StringRef EntryFnName) {
8778 if (OutlinedFn)
8779 return OutlinedFn;
8780
8781 assert(!M.getGlobalVariable(EntryFnName, true) &&
8782 "Named kernel already exists?");
8783 return new GlobalVariable(
8784 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8785 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8786}
8787
8789 TargetRegionEntryInfo &EntryInfo,
8790 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8791 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8792
8793 SmallString<64> EntryFnName;
8794 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8795
8796 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8797 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8798 if (!CBResult)
8799 return CBResult.takeError();
8800 OutlinedFn = *CBResult;
8801 } else {
8802 OutlinedFn = nullptr;
8803 }
8804
8805 // If this target outline function is not an offload entry, we don't need to
8806 // register it. This may be in the case of a false if clause, or if there are
8807 // no OpenMP targets.
8808 if (!IsOffloadEntry)
8809 return Error::success();
8810
8811 std::string EntryFnIDName =
8812 Config.isTargetDevice()
8813 ? std::string(EntryFnName)
8814 : createPlatformSpecificName({EntryFnName, "region_id"});
8815
8816 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8817 EntryFnName, EntryFnIDName);
8818 return Error::success();
8819}
8820
8822 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8823 StringRef EntryFnName, StringRef EntryFnIDName) {
8824 if (OutlinedFn)
8825 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8826 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8827 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8828 OffloadInfoManager.registerTargetRegionEntryInfo(
8829 EntryInfo, EntryAddr, OutlinedFnID,
8831 return OutlinedFnID;
8832}
8833
8835 const LocationDescription &Loc, InsertPointTy AllocaIP,
8836 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8837 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8838 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8839 omp::RuntimeFunction *MapperFunc,
8841 BodyGenTy BodyGenType)>
8842 BodyGenCB,
8843 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8844 if (!updateToLocation(Loc))
8845 return InsertPointTy();
8846
8847 Builder.restoreIP(CodeGenIP);
8848
8849 bool IsStandAlone = !BodyGenCB;
8850 MapInfosTy *MapInfo;
8851 // Generate the code for the opening of the data environment. Capture all the
8852 // arguments of the runtime call by reference because they are used in the
8853 // closing of the region.
8854 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8855 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8856 MapInfo = &GenMapInfoCB(Builder.saveIP());
8857 if (Error Err = emitOffloadingArrays(
8858 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
8859 /*IsNonContiguous=*/true, DeviceAddrCB))
8860 return Err;
8861
8862 TargetDataRTArgs RTArgs;
8864
8865 // Emit the number of elements in the offloading arrays.
8866 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8867
8868 // Source location for the ident struct
8869 if (!SrcLocInfo) {
8870 uint32_t SrcLocStrSize;
8871 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8872 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8873 }
8874
8875 SmallVector<llvm::Value *, 13> OffloadingArgs = {
8876 SrcLocInfo, DeviceID,
8877 PointerNum, RTArgs.BasePointersArray,
8878 RTArgs.PointersArray, RTArgs.SizesArray,
8879 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8880 RTArgs.MappersArray};
8881
8882 if (IsStandAlone) {
8883 assert(MapperFunc && "MapperFunc missing for standalone target data");
8884
8885 auto TaskBodyCB = [&](Value *, Value *,
8887 if (Info.HasNoWait) {
8888 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
8892 }
8893
8895 OffloadingArgs);
8896
8897 if (Info.HasNoWait) {
8898 BasicBlock *OffloadContBlock =
8899 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
8900 Function *CurFn = Builder.GetInsertBlock()->getParent();
8901 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
8902 Builder.restoreIP(Builder.saveIP());
8903 }
8904 return Error::success();
8905 };
8906
8907 bool RequiresOuterTargetTask = Info.HasNoWait;
8908 if (!RequiresOuterTargetTask)
8909 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
8910 /*TargetTaskAllocaIP=*/{}));
8911 else
8912 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
8913 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
8914 } else {
8915 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
8916 omp::OMPRTL___tgt_target_data_begin_mapper);
8917
8918 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
8919
8920 for (auto DeviceMap : Info.DevicePtrInfoMap) {
8921 if (isa<AllocaInst>(DeviceMap.second.second)) {
8922 auto *LI =
8923 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
8924 Builder.CreateStore(LI, DeviceMap.second.second);
8925 }
8926 }
8927
8928 // If device pointer privatization is required, emit the body of the
8929 // region here. It will have to be duplicated: with and without
8930 // privatization.
8931 InsertPointOrErrorTy AfterIP =
8932 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
8933 if (!AfterIP)
8934 return AfterIP.takeError();
8935 Builder.restoreIP(*AfterIP);
8936 }
8937 return Error::success();
8938 };
8939
8940 // If we need device pointer privatization, we need to emit the body of the
8941 // region with no privatization in the 'else' branch of the conditional.
8942 // Otherwise, we don't have to do anything.
8943 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8944 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8945 InsertPointOrErrorTy AfterIP =
8946 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
8947 if (!AfterIP)
8948 return AfterIP.takeError();
8949 Builder.restoreIP(*AfterIP);
8950 return Error::success();
8951 };
8952
8953 // Generate code for the closing of the data region.
8954 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8955 ArrayRef<BasicBlock *> DeallocBlocks) {
8956 TargetDataRTArgs RTArgs;
8957 Info.EmitDebug = !MapInfo->Names.empty();
8958 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
8959
8960 // Emit the number of elements in the offloading arrays.
8961 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8962
8963 // Source location for the ident struct
8964 if (!SrcLocInfo) {
8965 uint32_t SrcLocStrSize;
8966 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8967 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8968 }
8969
8970 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
8971 PointerNum, RTArgs.BasePointersArray,
8972 RTArgs.PointersArray, RTArgs.SizesArray,
8973 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8974 RTArgs.MappersArray};
8975 Function *EndMapperFunc =
8976 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
8977
8978 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
8979 return Error::success();
8980 };
8981
8982 // We don't have to do anything to close the region if the if clause evaluates
8983 // to false.
8984 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8985 ArrayRef<BasicBlock *> DeallocBlocks) {
8986 return Error::success();
8987 };
8988
8989 Error Err = [&]() -> Error {
8990 if (BodyGenCB) {
8991 Error Err = [&]() {
8992 if (IfCond)
8993 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
8994 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
8995 }();
8996
8997 if (Err)
8998 return Err;
8999
9000 // If we don't require privatization of device pointers, we emit the body
9001 // in between the runtime calls. This avoids duplicating the body code.
9002 InsertPointOrErrorTy AfterIP =
9003 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9004 if (!AfterIP)
9005 return AfterIP.takeError();
9006 restoreIPandDebugLoc(Builder, *AfterIP);
9007
9008 if (IfCond)
9009 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9010 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9011 }
9012 if (IfCond)
9013 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9014 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9015 }();
9016
9017 if (Err)
9018 return Err;
9019
9020 return Builder.saveIP();
9021}
9022
9025 bool IsGPUDistribute) {
9026 assert((IVSize == 32 || IVSize == 64) &&
9027 "IV size is not compatible with the omp runtime");
9028 RuntimeFunction Name;
9029 if (IsGPUDistribute)
9030 Name = IVSize == 32
9031 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9032 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9033 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9034 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9035 else
9036 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9037 : omp::OMPRTL___kmpc_for_static_init_4u)
9038 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9039 : omp::OMPRTL___kmpc_for_static_init_8u);
9040
9041 return getOrCreateRuntimeFunction(M, Name);
9042}
9043
9045 bool IVSigned) {
9046 assert((IVSize == 32 || IVSize == 64) &&
9047 "IV size is not compatible with the omp runtime");
9048 RuntimeFunction Name = IVSize == 32
9049 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9050 : omp::OMPRTL___kmpc_dispatch_init_4u)
9051 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9052 : omp::OMPRTL___kmpc_dispatch_init_8u);
9053
9054 return getOrCreateRuntimeFunction(M, Name);
9055}
9056
9058 bool IVSigned) {
9059 assert((IVSize == 32 || IVSize == 64) &&
9060 "IV size is not compatible with the omp runtime");
9061 RuntimeFunction Name = IVSize == 32
9062 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9063 : omp::OMPRTL___kmpc_dispatch_next_4u)
9064 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9065 : omp::OMPRTL___kmpc_dispatch_next_8u);
9066
9067 return getOrCreateRuntimeFunction(M, Name);
9068}
9069
9071 bool IVSigned) {
9072 assert((IVSize == 32 || IVSize == 64) &&
9073 "IV size is not compatible with the omp runtime");
9074 RuntimeFunction Name = IVSize == 32
9075 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9076 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9077 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9078 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9079
9080 return getOrCreateRuntimeFunction(M, Name);
9081}
9082
9084 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9085}
9086
9088 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9089 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9090
9091 DISubprogram *NewSP = Func->getSubprogram();
9092 if (!NewSP)
9093 return;
9094
9096
9097 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9098 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9099 // Only use cached variable if the arg number matches. This is important
9100 // so that DIVariable created for privatized variables are not discarded.
9101 if (NewVar && (arg == NewVar->getArg()))
9102 return NewVar;
9103
9105 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9106 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9107 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9108 return NewVar;
9109 };
9110
9111 auto UpdateDebugRecord = [&](auto *DR) {
9112 DILocalVariable *OldVar = DR->getVariable();
9113 unsigned ArgNo = 0;
9114 for (auto Loc : DR->location_ops()) {
9115 auto Iter = ValueReplacementMap.find(Loc);
9116 if (Iter != ValueReplacementMap.end()) {
9117 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9118 ArgNo = std::get<1>(Iter->second) + 1;
9119 }
9120 }
9121 if (ArgNo != 0)
9122 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9123 };
9124
9126 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9127 if (DVR->getNumVariableLocationOps() != 1u) {
9128 DVR->setKillLocation();
9129 return;
9130 }
9131 Value *Loc = DVR->getVariableLocationOp(0u);
9132 BasicBlock *CurBB = DVR->getParent();
9133 BasicBlock *RequiredBB = nullptr;
9134
9135 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9136 RequiredBB = LocInst->getParent();
9137 else if (isa<llvm::Argument>(Loc))
9138 RequiredBB = &DVR->getFunction()->getEntryBlock();
9139
9140 if (RequiredBB && RequiredBB != CurBB) {
9141 assert(!RequiredBB->empty());
9142 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9143 RequiredBB->back().getIterator());
9144 DVRsToDelete.push_back(DVR);
9145 }
9146 };
9147
9148 // The location and scope of variable intrinsics and records still point to
9149 // the parent function of the target region. Update them.
9150 for (Instruction &I : instructions(Func)) {
9152 "Unexpected debug intrinsic");
9153 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9154 UpdateDebugRecord(&DVR);
9155 MoveDebugRecordToCorrectBlock(&DVR);
9156 }
9157 }
9158 for (auto *DVR : DVRsToDelete)
9159 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9160 // An extra argument is passed to the device. Create the debug data for it.
9161 if (OMPBuilder.Config.isTargetDevice()) {
9162 DICompileUnit *CU = NewSP->getUnit();
9163 Module *M = Func->getParent();
9164 DIBuilder DB(*M, true, CU);
9165 DIType *VoidPtrTy =
9166 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9167 unsigned ArgNo = Func->arg_size();
9168 DILocalVariable *Var = DB.createParameterVariable(
9169 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9170 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9171 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9172 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9173 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9174 &(*Func->begin()));
9175 }
9176}
9177
9179 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9180 return cast<Operator>(V)->getOperand(0);
9181 return V;
9182}
9183
9185 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9187 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9190 SmallVector<Type *> ParameterTypes;
9191 if (OMPBuilder.Config.isTargetDevice()) {
9192 // All parameters to target devices are passed as pointers
9193 // or i64. This assumes 64-bit address spaces/pointers.
9194 for (auto &Arg : Inputs)
9195 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9196 ? Arg->getType()
9197 : Type::getInt64Ty(Builder.getContext()));
9198 } else {
9199 for (auto &Arg : Inputs)
9200 ParameterTypes.push_back(Arg->getType());
9201 }
9202
9203 // The implicit dyn_ptr argument is always the last parameter on both host
9204 // and device so the argument counts match without runtime manipulation.
9205 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9206 ParameterTypes.push_back(PtrTy);
9207
9208 auto BB = Builder.GetInsertBlock();
9209 auto M = BB->getModule();
9210 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9211 /*isVarArg*/ false);
9212 auto Func =
9213 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9214
9215 // Forward target-cpu and target-features function attributes from the
9216 // original function to the new outlined function.
9217 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9218
9219 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9220 if (TargetCpuAttr.isStringAttribute())
9221 Func->addFnAttr(TargetCpuAttr);
9222
9223 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9224 if (TargetFeaturesAttr.isStringAttribute())
9225 Func->addFnAttr(TargetFeaturesAttr);
9226
9227 if (OMPBuilder.Config.isTargetDevice()) {
9228 Value *ExecMode =
9229 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9230 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9231 }
9232
9233 // Save insert point.
9234 IRBuilder<>::InsertPointGuard IPG(Builder);
9235 // We will generate the entries in the outlined function but the debug
9236 // location may still be pointing to the parent function. Reset it now.
9237 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9238
9239 // Generate the region into the function.
9240 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9241 Builder.SetInsertPoint(EntryBB);
9242
9243 // Insert target init call in the device compilation pass.
9244 if (OMPBuilder.Config.isTargetDevice())
9245 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9246
9247 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9248
9249 // As we embed the user code in the middle of our target region after we
9250 // generate entry code, we must move what allocas we can into the entry
9251 // block to avoid possible breaking optimisations for device
9252 if (OMPBuilder.Config.isTargetDevice())
9254
9255 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9256 BasicBlock *OutlinedBodyBB =
9257 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9259 Builder.saveIP(),
9260 OpenMPIRBuilder::InsertPointTy(OutlinedBodyBB, OutlinedBodyBB->begin()),
9261 ExitBB);
9262 if (!AfterIP)
9263 return AfterIP.takeError();
9264 Builder.SetInsertPoint(ExitBB);
9265
9266 // Insert target deinit call in the device compilation pass.
9267 if (OMPBuilder.Config.isTargetDevice())
9268 OMPBuilder.createTargetDeinit(Builder);
9269
9270 // Insert return instruction.
9271 Builder.CreateRetVoid();
9272
9273 // New Alloca IP at entry point of created device function.
9274 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9275 auto AllocaIP = Builder.saveIP();
9276
9277 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());
9278
9279 // Do not include the artificial dyn_ptr argument.
9280 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9281
9283
9284 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9285 // Things like GEP's can come in the form of Constants. Constants and
9286 // ConstantExpr's do not have access to the knowledge of what they're
9287 // contained in, so we must dig a little to find an instruction so we
9288 // can tell if they're used inside of the function we're outlining. We
9289 // also replace the original constant expression with a new instruction
9290 // equivalent; an instruction as it allows easy modification in the
9291 // following loop, as we can now know the constant (instruction) is
9292 // owned by our target function and replaceUsesOfWith can now be invoked
9293 // on it (cannot do this with constants it seems). A brand new one also
9294 // allows us to be cautious as it is perhaps possible the old expression
9295 // was used inside of the function but exists and is used externally
9296 // (unlikely by the nature of a Constant, but still).
9297 // NOTE: We cannot remove dead constants that have been rewritten to
9298 // instructions at this stage, we run the risk of breaking later lowering
9299 // by doing so as we could still be in the process of lowering the module
9300 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9301 // constants we have created rewritten versions of.
9302 if (auto *Const = dyn_cast<Constant>(Input))
9303 convertUsersOfConstantsToInstructions(Const, Func, false);
9304
9305 // Collect users before iterating over them to avoid invalidating the
9306 // iteration in case a user uses Input more than once (e.g. a call
9307 // instruction).
9308 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9309 // Collect all the instructions
9311 if (auto *Instr = dyn_cast<Instruction>(User))
9312 if (Instr->getFunction() == Func)
9313 Instr->replaceUsesOfWith(Input, InputCopy);
9314 };
9315
9316 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9317
9318 // Rewrite uses of input valus to parameters.
9319 for (auto InArg : zip(Inputs, ArgRange)) {
9320 Value *Input = std::get<0>(InArg);
9321 Argument &Arg = std::get<1>(InArg);
9322 Value *InputCopy = nullptr;
9323
9324 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9325 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(),
9326 OpenMPIRBuilder::InsertPointTy(ExitBB, ExitBB->begin()));
9327 if (!AfterIP)
9328 return AfterIP.takeError();
9329 Builder.restoreIP(*AfterIP);
9330 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9331
9332 // In certain cases a Global may be set up for replacement, however, this
9333 // Global may be used in multiple arguments to the kernel, just segmented
9334 // apart, for example, if we have a global array, that is sectioned into
9335 // multiple mappings (technically not legal in OpenMP, but there is a case
9336 // in Fortran for Common Blocks where this is neccesary), we will end up
9337 // with GEP's into this array inside the kernel, that refer to the Global
9338 // but are technically separate arguments to the kernel for all intents and
9339 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9340 // index, it will fold into an referal to the Global, if we then encounter
9341 // this folded GEP during replacement all of the references to the
9342 // Global in the kernel will be replaced with the argument we have generated
9343 // that corresponds to it, including any other GEP's that refer to the
9344 // Global that may be other arguments. This will invalidate all of the other
9345 // preceding mapped arguments that refer to the same global that may be
9346 // separate segments. To prevent this, we defer global processing until all
9347 // other processing has been performed.
9350 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9351 continue;
9352 }
9353
9355 continue;
9356
9357 ReplaceValue(Input, InputCopy, Func);
9358 }
9359
9360 // Replace all of our deferred Input values, currently just Globals.
9361 for (auto Deferred : DeferredReplacement)
9362 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9363
9364 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9365 ValueReplacementMap);
9366 return Func;
9367}
9368/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9369/// of pointers containing shared data between the parent task and the created
9370/// task.
9372 IRBuilderBase &Builder,
9373 Value *TaskWithPrivates,
9374 Type *TaskWithPrivatesTy) {
9375
9376 Type *TaskTy = OMPIRBuilder.Task;
9377 LLVMContext &Ctx = Builder.getContext();
9378 Value *TaskT =
9379 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9380 Value *Shareds = TaskT;
9381 // TaskWithPrivatesTy can be one of the following
9382 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9383 // %struct.privates }
9384 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9385 //
9386 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9387 // its first member has to be the task descriptor. TaskTy is the type of the
9388 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9389 // first member of TaskT, gives us the pointer to shared data.
9390 if (TaskWithPrivatesTy != TaskTy)
9391 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9392 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9393}
9394/// Create an entry point for a target task with the following.
9395/// It'll have the following signature
9396/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9397/// This function is called from emitTargetTask once the
9398/// code to launch the target kernel has been outlined already.
9399/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9400/// into the task structure so that the deferred target task can access this
9401/// data even after the stack frame of the generating task has been rolled
9402/// back. Offloading arrays contain base pointers, pointers, sizes etc
9403/// of the data that the target kernel will access. These in effect are the
9404/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9406 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9407 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9408 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9409
9410 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9411 // This is because PrivatesTy is the type of the structure in which
9412 // we pass the offloading arrays to the deferred target task.
9413 assert((!NumOffloadingArrays || PrivatesTy) &&
9414 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9415 "to privatize");
9416
9417 Module &M = OMPBuilder.M;
9418 // KernelLaunchFunction is the target launch function, i.e.
9419 // the function that sets up kernel arguments and calls
9420 // __tgt_target_kernel to launch the kernel on the device.
9421 //
9422 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9423
9424 // StaleCI is the CallInst which is the call to the outlined
9425 // target kernel launch function. If there are local live-in values
9426 // that the outlined function uses then these are aggregated into a structure
9427 // which is passed as the second argument. If there are no local live-in
9428 // values or if all values used by the outlined kernel are global variables,
9429 // then there's only one argument, the threadID. So, StaleCI can be
9430 //
9431 // %structArg = alloca { ptr, ptr }, align 8
9432 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9433 // store ptr %20, ptr %gep_, align 8
9434 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9435 // store ptr %21, ptr %gep_8, align 8
9436 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9437 //
9438 // OR
9439 //
9440 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9442 StaleCI->getIterator());
9443
9444 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9445
9446 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9447 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9448 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9449
9450 auto ProxyFnTy =
9451 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9452 /* isVarArg */ false);
9453 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9454 ".omp_target_task_proxy_func",
9455 Builder.GetInsertBlock()->getModule());
9456 Value *ThreadId = ProxyFn->getArg(0);
9457 Value *TaskWithPrivates = ProxyFn->getArg(1);
9458 ThreadId->setName("thread.id");
9459 TaskWithPrivates->setName("task");
9460
9461 bool HasShareds = SharedArgsOperandNo > 0;
9462 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9463 BasicBlock *EntryBB =
9464 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9465 Builder.SetInsertPoint(EntryBB);
9466
9467 SmallVector<Value *> KernelLaunchArgs;
9468 KernelLaunchArgs.reserve(StaleCI->arg_size());
9469 KernelLaunchArgs.push_back(ThreadId);
9470
9471 if (HasOffloadingArrays) {
9472 assert(TaskTy != TaskWithPrivatesTy &&
9473 "If there are offloading arrays to pass to the target"
9474 "TaskTy cannot be the same as TaskWithPrivatesTy");
9475 (void)TaskTy;
9476 Value *Privates =
9477 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9478 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9479 KernelLaunchArgs.push_back(
9480 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9481 }
9482
9483 if (HasShareds) {
9484 auto *ArgStructAlloca =
9485 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9486 assert(ArgStructAlloca &&
9487 "Unable to find the alloca instruction corresponding to arguments "
9488 "for extracted function");
9489 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9490 std::optional<TypeSize> ArgAllocSize =
9491 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9492 assert(ArgStructType && ArgAllocSize &&
9493 "Unable to determine size of arguments for extracted function");
9494 uint64_t StructSize = ArgAllocSize->getFixedValue();
9495
9496 AllocaInst *NewArgStructAlloca =
9497 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9498
9499 Value *SharedsSize = Builder.getInt64(StructSize);
9500
9502 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9503
9504 Builder.CreateMemCpy(
9505 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9506 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9507 KernelLaunchArgs.push_back(NewArgStructAlloca);
9508 }
9509 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9510 Builder.CreateRetVoid();
9511 return ProxyFn;
9512}
9514
9515 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9516 return GEP->getSourceElementType();
9517 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9518 return Alloca->getAllocatedType();
9519
9520 llvm_unreachable("Unhandled Instruction type");
9521 return nullptr;
9522}
9523// This function returns a struct that has at most two members.
9524// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9525// descriptor. The second member, if needed, is a struct containing arrays
9526// that need to be passed to the offloaded target kernel. For example,
9527// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9528// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9529// respectively, then the types created by this function are
9530//
9531// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9532// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9533// %struct.privates }
9534// %struct.task_with_privates is returned by this function.
9535// If there aren't any offloading arrays to pass to the target kernel,
9536// %struct.kmp_task_ompbuilder_t is returned.
9537static StructType *
9539 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9540
9541 if (OffloadingArraysToPrivatize.empty())
9542 return OMPIRBuilder.Task;
9543
9544 SmallVector<Type *, 4> StructFieldTypes;
9545 for (Value *V : OffloadingArraysToPrivatize) {
9546 assert(V->getType()->isPointerTy() &&
9547 "Expected pointer to array to privatize. Got a non-pointer value "
9548 "instead");
9549 Type *ArrayTy = getOffloadingArrayType(V);
9550 assert(ArrayTy && "ArrayType cannot be nullptr");
9551 StructFieldTypes.push_back(ArrayTy);
9552 }
9553 StructType *PrivatesStructTy =
9554 StructType::create(StructFieldTypes, "struct.privates");
9555 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9556 "struct.task_with_privates");
9557}
9559 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9560 TargetRegionEntryInfo &EntryInfo,
9562 Function *&OutlinedFn, Constant *&OutlinedFnID,
9566
9567 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9568 [&](StringRef EntryFnName) {
9569 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9570 EntryFnName, Inputs, CBFunc,
9571 ArgAccessorFuncCB);
9572 };
9573
9574 return OMPBuilder.emitTargetRegionFunction(
9575 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9576 OutlinedFnID);
9577}
9578
9580 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9582 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9583 bool HasNoWait) {
9584
9585 // The following explains the code-gen scenario for the `target` directive. A
9586 // similar scneario is followed for other device-related directives (e.g.
9587 // `target enter data`) but in similar fashion since we only need to emit task
9588 // that encapsulates the proper runtime call.
9589 //
9590 // When we arrive at this function, the target region itself has been
9591 // outlined into the function OutlinedFn.
9592 // So at ths point, for
9593 // --------------------------------------------------------------
9594 // void user_code_that_offloads(...) {
9595 // omp target depend(..) map(from:a) map(to:b) private(i)
9596 // do i = 1, 10
9597 // a(i) = b(i) + n
9598 // }
9599 //
9600 // --------------------------------------------------------------
9601 //
9602 // we have
9603 //
9604 // --------------------------------------------------------------
9605 //
9606 // void user_code_that_offloads(...) {
9607 // %.offload_baseptrs = alloca [2 x ptr], align 8
9608 // %.offload_ptrs = alloca [2 x ptr], align 8
9609 // %.offload_mappers = alloca [2 x ptr], align 8
9610 // ;; target region has been outlined and now we need to
9611 // ;; offload to it via a target task.
9612 // }
9613 // void outlined_device_function(ptr a, ptr b, ptr n) {
9614 // n = *n_ptr;
9615 // do i = 1, 10
9616 // a(i) = b(i) + n
9617 // }
9618 //
9619 // We have to now do the following
9620 // (i) Make an offloading call to outlined_device_function using the OpenMP
9621 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9622 // emitted by emitKernelLaunch
9623 // (ii) Create a task entry point function that calls kernel_launch_function
9624 // and is the entry point for the target task. See
9625 // '@.omp_target_task_proxy_func in the pseudocode below.
9626 // (iii) Create a task with the task entry point created in (ii)
9627 //
9628 // That is we create the following
9629 // struct task_with_privates {
9630 // struct kmp_task_ompbuilder_t task_struct;
9631 // struct privates {
9632 // [2 x ptr] ; baseptrs
9633 // [2 x ptr] ; ptrs
9634 // [2 x i64] ; sizes
9635 // }
9636 // }
9637 // void user_code_that_offloads(...) {
9638 // %.offload_baseptrs = alloca [2 x ptr], align 8
9639 // %.offload_ptrs = alloca [2 x ptr], align 8
9640 // %.offload_sizes = alloca [2 x i64], align 8
9641 //
9642 // %structArg = alloca { ptr, ptr, ptr }, align 8
9643 // %strucArg[0] = a
9644 // %strucArg[1] = b
9645 // %strucArg[2] = &n
9646 //
9647 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9648 // sizeof(kmp_task_ompbuilder_t),
9649 // sizeof(structArg),
9650 // @.omp_target_task_proxy_func,
9651 // ...)
9652 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9653 // sizeof(structArg))
9654 // memcpy(target_task_with_privates->privates->baseptrs,
9655 // offload_baseptrs, sizeof(offload_baseptrs)
9656 // memcpy(target_task_with_privates->privates->ptrs,
9657 // offload_ptrs, sizeof(offload_ptrs)
9658 // memcpy(target_task_with_privates->privates->sizes,
9659 // offload_sizes, sizeof(offload_sizes)
9660 // dependencies_array = ...
9661 // ;; if nowait not present
9662 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9663 // call @__kmpc_omp_task_begin_if0(...)
9664 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9665 // %target_task_with_privates)
9666 // call @__kmpc_omp_task_complete_if0(...)
9667 // }
9668 //
9669 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9670 // ptr %task) {
9671 // %structArg = alloca {ptr, ptr, ptr}
9672 // %task_ptr = getelementptr(%task, 0, 0)
9673 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9674 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9675 //
9676 // %offloading_arrays = getelementptr(%task, 0, 1)
9677 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9678 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9679 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9680 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9681 // %offload_sizes, %structArg)
9682 // }
9683 //
9684 // We need the proxy function because the signature of the task entry point
9685 // expected by kmpc_omp_task is always the same and will be different from
9686 // that of the kernel_launch function.
9687 //
9688 // kernel_launch_function is generated by emitKernelLaunch and has the
9689 // always_inline attribute. For this example, it'll look like so:
9690 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9691 // %offload_sizes, %structArg) alwaysinline {
9692 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9693 // ; load aggregated data from %structArg
9694 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9695 // ; offload_sizes
9696 // call i32 @__tgt_target_kernel(...,
9697 // outlined_device_function,
9698 // ptr %kernel_args)
9699 // }
9700 // void outlined_device_function(ptr a, ptr b, ptr n) {
9701 // n = *n_ptr;
9702 // do i = 1, 10
9703 // a(i) = b(i) + n
9704 // }
9705 //
9706 BasicBlock *TargetTaskBodyBB =
9707 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9708 BasicBlock *TargetTaskAllocaBB =
9709 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9710
9711 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB,
9712 TargetTaskAllocaBB->begin());
9713 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB, TargetTaskBodyBB->begin());
9714
9715 auto OI = std::make_unique<OutlineInfo>();
9716 OI->EntryBB = TargetTaskAllocaBB;
9717 OI->OuterAllocBB = AllocaIP.getBlock();
9718
9719 // Add the thread ID argument.
9721 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9722 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9723
9724 // Generate the task body which will subsequently be outlined.
9725 Builder.restoreIP(TargetTaskBodyIP);
9726 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9727 return Err;
9728
9729 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9730 // it is given. These blocks are enumerated by
9731 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9732 // to be outside the region. In other words, OI.ExitBlock is expected to be
9733 // the start of the region after the outlining. We used to set OI.ExitBlock
9734 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9735 // except when the task body is a single basic block. In that case,
9736 // OI.ExitBlock is set to the single task body block and will get left out of
9737 // the outlining process. So, simply create a new empty block to which we
9738 // uncoditionally branch from where TaskBodyCB left off
9739 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9740 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9741 /*IsFinished=*/true);
9742
9743 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9744 bool NeedsTargetTask = HasNoWait && DeviceID;
9745 if (NeedsTargetTask) {
9746 for (auto *V :
9747 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9748 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9749 RTArgs.SizesArray}) {
9751 OffloadingArraysToPrivatize.push_back(V);
9752 OI->ExcludeArgsFromAggregate.push_back(V);
9753 }
9754 }
9755 }
9756 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9757 DeviceID, OffloadingArraysToPrivatize](
9758 Function &OutlinedFn) mutable {
9759 assert(OutlinedFn.hasOneUse() &&
9760 "there must be a single user for the outlined function");
9761
9762 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9763
9764 // The first argument of StaleCI is always the thread id.
9765 // The next few arguments are the pointers to offloading arrays
9766 // if any. (see OffloadingArraysToPrivatize)
9767 // Finally, all other local values that are live-in into the outlined region
9768 // end up in a structure whose pointer is passed as the last argument. This
9769 // piece of data is passed in the "shared" field of the task structure. So,
9770 // we know we have to pass shareds to the task if the number of arguments is
9771 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9772 // thread id. Further, for safety, we assert that the number of arguments of
9773 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9774 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9775 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9776 assert((!HasShareds ||
9777 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9778 "Wrong number of arguments for StaleCI when shareds are present");
9779 int SharedArgOperandNo =
9780 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9781
9782 StructType *TaskWithPrivatesTy =
9783 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9784 StructType *PrivatesTy = nullptr;
9785
9786 if (!OffloadingArraysToPrivatize.empty())
9787 PrivatesTy =
9788 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9789
9791 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9792 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9793
9794 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9795 << "\n");
9796
9797 Builder.SetInsertPoint(StaleCI);
9798
9799 // Gather the arguments for emitting the runtime call.
9800 uint32_t SrcLocStrSize;
9801 Constant *SrcLocStr =
9803 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9804
9805 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9806 //
9807 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9808 // the DeviceID to the deferred task and also since
9809 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9810 Function *TaskAllocFn =
9811 !NeedsTargetTask
9812 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9814 OMPRTL___kmpc_omp_target_task_alloc);
9815
9816 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9817 // call.
9818 Value *ThreadID = getOrCreateThreadID(Ident);
9819
9820 // Argument - `sizeof_kmp_task_t` (TaskSize)
9821 // Tasksize refers to the size in bytes of kmp_task_t data structure
9822 // plus any other data to be passed to the target task, if any, which
9823 // is packed into a struct. kmp_task_t and the struct so created are
9824 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9825 Value *TaskSize = Builder.getInt64(
9826 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9827
9828 // Argument - `sizeof_shareds` (SharedsSize)
9829 // SharedsSize refers to the shareds array size in the kmp_task_t data
9830 // structure.
9831 Value *SharedsSize = Builder.getInt64(0);
9832 if (HasShareds) {
9833 auto *ArgStructAlloca =
9834 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9835 assert(ArgStructAlloca &&
9836 "Unable to find the alloca instruction corresponding to arguments "
9837 "for extracted function");
9838 std::optional<TypeSize> ArgAllocSize =
9839 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9840 assert(ArgAllocSize &&
9841 "Unable to determine size of arguments for extracted function");
9842 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9843 }
9844
9845 // Argument - `flags`
9846 // Task is tied iff (Flags & 1) == 1.
9847 // Task is untied iff (Flags & 1) == 0.
9848 // Task is final iff (Flags & 2) == 2.
9849 // Task is not final iff (Flags & 2) == 0.
9850 // A target task is not final and is untied.
9851 Value *Flags = Builder.getInt32(0);
9852
9853 // Emit the @__kmpc_omp_task_alloc runtime call
9854 // The runtime call returns a pointer to an area where the task captured
9855 // variables must be copied before the task is run (TaskData)
9856 CallInst *TaskData = nullptr;
9857
9858 SmallVector<llvm::Value *> TaskAllocArgs = {
9859 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
9860 /*flags=*/Flags,
9861 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
9862 /*task_func=*/ProxyFn};
9863
9864 if (NeedsTargetTask) {
9865 assert(DeviceID && "Expected non-empty device ID.");
9866 TaskAllocArgs.push_back(DeviceID);
9867 }
9868
9869 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
9870
9871 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
9872 if (HasShareds) {
9873 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
9875 *this, Builder, TaskData, TaskWithPrivatesTy);
9876 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
9877 SharedsSize);
9878 }
9879 if (!OffloadingArraysToPrivatize.empty()) {
9880 Value *Privates =
9881 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
9882 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
9883 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
9884 [[maybe_unused]] Type *ArrayType =
9885 getOffloadingArrayType(PtrToPrivatize);
9886 assert(ArrayType && "ArrayType cannot be nullptr");
9887
9888 Type *ElementType = PrivatesTy->getElementType(i);
9889 assert(ElementType == ArrayType &&
9890 "ElementType should match ArrayType");
9891 (void)ArrayType;
9892
9893 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
9894 Builder.CreateMemCpy(
9895 Dst, Alignment, PtrToPrivatize, Alignment,
9896 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
9897 }
9898 }
9899
9900 Value *DepArray = nullptr;
9901 Value *NumDeps = nullptr;
9902 if (Dependencies.DepArray) {
9903 DepArray = Dependencies.DepArray;
9904 NumDeps = Dependencies.NumDeps;
9905 } else if (!Dependencies.Deps.empty()) {
9906 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
9907 NumDeps = Builder.getInt32(Dependencies.Deps.size());
9908 }
9909
9910 // ---------------------------------------------------------------
9911 // V5.2 13.8 target construct
9912 // If the nowait clause is present, execution of the target task
9913 // may be deferred. If the nowait clause is not present, the target task is
9914 // an included task.
9915 // ---------------------------------------------------------------
9916 // The above means that the lack of a nowait on the target construct
9917 // translates to '#pragma omp task if(0)'
9918 if (!NeedsTargetTask) {
9919 if (DepArray) {
9920 Function *TaskWaitFn =
9921 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
9923 TaskWaitFn,
9924 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
9925 /*ndeps=*/NumDeps,
9926 /*dep_list=*/DepArray,
9927 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
9928 /*noalias_dep_list=*/
9930 }
9931 // Included task.
9932 Function *TaskBeginFn =
9933 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
9934 Function *TaskCompleteFn =
9935 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
9936 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
9937 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
9938 CI->setDebugLoc(StaleCI->getDebugLoc());
9939 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
9940 } else if (DepArray) {
9941 // HasNoWait - meaning the task may be deferred. Call
9942 // __kmpc_omp_task_with_deps if there are dependencies,
9943 // else call __kmpc_omp_task
9944 Function *TaskFn =
9945 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
9947 TaskFn,
9948 {Ident, ThreadID, TaskData, NumDeps, DepArray,
9949 ConstantInt::get(Builder.getInt32Ty(), 0),
9951 } else {
9952 // Emit the @__kmpc_omp_task runtime call to spawn the task
9953 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
9954 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
9955 }
9956
9957 StaleCI->eraseFromParent();
9958 for (Instruction *I : llvm::reverse(ToBeDeleted))
9959 I->eraseFromParent();
9960 };
9961 addOutlineInfo(std::move(OI));
9962
9963 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
9964 << *(Builder.GetInsertBlock()) << "\n");
9965 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
9966 << *(Builder.GetInsertBlock()->getParent()->getParent())
9967 << "\n");
9968 return Builder.saveIP();
9969}
9970
9972 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
9973 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
9974 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
9975 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
9976 if (Error Err =
9977 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
9978 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
9979 return Err;
9980 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
9981 return Error::success();
9982}
9983
9984static void emitTargetCall(
9985 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9990 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
9994 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
9995 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
9996 // Generate a function call to the host fallback implementation of the target
9997 // region. This is called by the host when no offload entry was generated for
9998 // the target region and when the offloading call fails at runtime.
9999 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10001 Builder.restoreIP(IP);
10002 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10003 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10004 FallbackArgs.push_back(
10005 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10006 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10007 return Builder.saveIP();
10008 };
10009
10010 bool HasDependencies = !Dependencies.empty();
10011 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10012
10014
10015 auto TaskBodyCB =
10016 [&](Value *DeviceID, Value *RTLoc,
10017 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10018 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10019 // produce any.
10021 // emitKernelLaunch makes the necessary runtime call to offload the
10022 // kernel. We then outline all that code into a separate function
10023 // ('kernel_launch_function' in the pseudo code above). This function is
10024 // then called by the target task proxy function (see
10025 // '@.omp_target_task_proxy_func' in the pseudo code above)
10026 // "@.omp_target_task_proxy_func' is generated by
10027 // emitTargetTaskProxyFunction.
10028 if (OutlinedFnID && DeviceID)
10029 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10030 EmitTargetCallFallbackCB, KArgs,
10031 DeviceID, RTLoc, TargetTaskAllocaIP);
10032
10033 // We only need to do the outlining if `DeviceID` is set to avoid calling
10034 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10035 // generating the `else` branch of an `if` clause.
10036 //
10037 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10038 // In this case, we execute the host implementation directly.
10039 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10040 }());
10041
10042 OMPBuilder.Builder.restoreIP(AfterIP);
10043 return Error::success();
10044 };
10045
10046 auto &&EmitTargetCallElse =
10047 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10049 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10050 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10051 // produce any.
10053 if (RequiresOuterTargetTask) {
10054 // Arguments that are intended to be directly forwarded to an
10055 // emitKernelLaunch call are pased as nullptr, since
10056 // OutlinedFnID=nullptr results in that call not being done.
10058 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10059 /*RTLoc=*/nullptr, AllocaIP,
10060 Dependencies, EmptyRTArgs, HasNoWait);
10061 }
10062 return EmitTargetCallFallbackCB(Builder.saveIP());
10063 }());
10064
10065 Builder.restoreIP(AfterIP);
10066 return Error::success();
10067 };
10068
10069 auto &&EmitTargetCallThen =
10070 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10072 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10073 Info.HasNoWait = HasNoWait;
10074 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10075
10077 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10078 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10079 /*IsNonContiguous=*/true,
10080 /*ForEndCall=*/false))
10081 return Err;
10082
10083 SmallVector<Value *, 3> NumTeamsC;
10084 for (auto [DefaultVal, RuntimeVal] :
10085 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10086 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10087 : Builder.getInt32(DefaultVal));
10088
10089 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10090 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10091 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10092 if (Clause)
10093 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10094 /*isSigned=*/false);
10095 return Clause;
10096 };
10097 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10098 if (Clause)
10099 Result =
10100 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10101 Result, Clause)
10102 : Clause;
10103 };
10104
10105 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10106 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10107 SmallVector<Value *, 3> NumThreadsC;
10108 Value *MaxThreadsClause =
10109 RuntimeAttrs.TeamsThreadLimit.size() == 1
10110 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads)
10111 : nullptr;
10112
10113 for (auto [TeamsVal, TargetVal] : zip_equal(
10114 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10115 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10116 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10117
10118 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10119 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10120
10121 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10122 }
10123
10124 unsigned NumTargetItems = Info.NumberOfPtrs;
10125 uint32_t SrcLocStrSize;
10126 Constant *SrcLocStr = OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10127 Value *RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10128 llvm::omp::IdentFlag(0), 0);
10129
10130 Value *TripCount = RuntimeAttrs.LoopTripCount
10131 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10132 Builder.getInt64Ty(),
10133 /*isSigned=*/false)
10134 : Builder.getInt64(0);
10135
10136 // Request zero groupprivate bytes by default.
10137 if (!DynCGroupMem)
10138 DynCGroupMem = Builder.getInt32(0);
10139
10141 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10142 HasNoWait, /*StrictBlocksAndThreads=*/false, DynCGroupMemFallback);
10143
10144 // Assume no error was returned because TaskBodyCB and
10145 // EmitTargetCallFallbackCB don't produce any.
10147 // The presence of certain clauses on the target directive require the
10148 // explicit generation of the target task.
10149 if (RequiresOuterTargetTask)
10150 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10151 RTLoc, AllocaIP, Dependencies,
10152 KArgs.RTArgs, Info.HasNoWait);
10153
10154 return OMPBuilder.emitKernelLaunch(
10155 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10156 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10157 }());
10158
10159 Builder.restoreIP(AfterIP);
10160 return Error::success();
10161 };
10162
10163 // If we don't have an ID for the target region, it means an offload entry
10164 // wasn't created. In this case we just run the host fallback directly and
10165 // ignore any potential 'if' clauses.
10166 if (!OutlinedFnID) {
10167 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10168 return;
10169 }
10170
10171 // If there's no 'if' clause, only generate the kernel launch code path.
10172 if (!IfCond) {
10173 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10174 return;
10175 }
10176
10177 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10178 EmitTargetCallElse, AllocaIP));
10179}
10180
10182 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10183 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10184 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10185 const TargetKernelDefaultAttrs &DefaultAttrs,
10186 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10187 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10190 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10191 bool HasNowait, Value *DynCGroupMem,
10192 OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10193
10194 if (!updateToLocation(Loc))
10195 return InsertPointTy();
10196
10197 Builder.restoreIP(CodeGenIP);
10198
10199 Function *OutlinedFn;
10200 Constant *OutlinedFnID = nullptr;
10201 // The target region is outlined into its own function. The LLVM IR for
10202 // the target region itself is generated using the callbacks CBFunc
10203 // and ArgAccessorFuncCB
10205 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10206 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB))
10207 return Err;
10208
10209 // If we are not on the target device, then we need to generate code
10210 // to make a remote call (offload) to the previously outlined function
10211 // that represents the target region. Do that now.
10212 if (!Config.isTargetDevice())
10213 emitTargetCall(*this, Builder, AllocaIP, DeallocBlocks, Info, DefaultAttrs,
10214 RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID, Inputs,
10215 GenMapInfoCB, CustomMapperCB, Dependencies, HasNowait,
10216 DynCGroupMem, DynCGroupMemFallback);
10217 return Builder.saveIP();
10218}
10219
10220std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10221 StringRef FirstSeparator,
10222 StringRef Separator) {
10223 SmallString<128> Buffer;
10224 llvm::raw_svector_ostream OS(Buffer);
10225 StringRef Sep = FirstSeparator;
10226 for (StringRef Part : Parts) {
10227 OS << Sep << Part;
10228 Sep = Separator;
10229 }
10230 return OS.str().str();
10231}
10232
10233std::string
10235 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10236 Config.separator());
10237}
10238
10240 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10241 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10242 if (Elem.second) {
10243 assert(Elem.second->getValueType() == Ty &&
10244 "OMP internal variable has different type than requested");
10245 } else {
10246 // TODO: investigate the appropriate linkage type used for the global
10247 // variable for possibly changing that to internal or private, or maybe
10248 // create different versions of the function for different OMP internal
10249 // variables.
10250 const DataLayout &DL = M.getDataLayout();
10251 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10252 // default global AS is 1.
10253 // See double-target-call-with-declare-target.f90 and
10254 // declare-target-vars-in-target-region.f90 libomptarget
10255 // tests.
10256 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10257 : M.getTargetTriple().isAMDGPU()
10258 ? 0
10259 : DL.getDefaultGlobalsAddressSpace();
10260 auto Linkage = this->M.getTargetTriple().getArch() == Triple::wasm32
10263 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10264 Constant::getNullValue(Ty), Elem.first(),
10265 /*InsertBefore=*/nullptr,
10266 GlobalValue::NotThreadLocal, AddressSpaceVal);
10267 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10268 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10269 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10270 Elem.second = GV;
10271 }
10272
10273 return Elem.second;
10274}
10275
10276Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10277 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10278 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10279 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10280}
10281
10283 LLVMContext &Ctx = Builder.getContext();
10284 Value *Null =
10285 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10286 Value *SizeGep =
10287 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10288 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10289 return SizePtrToInt;
10290}
10291
10294 std::string VarName) {
10295 llvm::Constant *MaptypesArrayInit =
10296 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10297 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10298 M, MaptypesArrayInit->getType(),
10299 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10300 VarName);
10301 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10302 return MaptypesArrayGlobal;
10303}
10304
10306 InsertPointTy AllocaIP,
10307 unsigned NumOperands,
10308 struct MapperAllocas &MapperAllocas) {
10309 if (!updateToLocation(Loc))
10310 return;
10311
10312 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10313 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10314 Builder.restoreIP(AllocaIP);
10315 AllocaInst *ArgsBase = Builder.CreateAlloca(
10316 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10317 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10318 ".offload_ptrs");
10319 AllocaInst *ArgSizes = Builder.CreateAlloca(
10320 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10322 MapperAllocas.ArgsBase = ArgsBase;
10323 MapperAllocas.Args = Args;
10324 MapperAllocas.ArgSizes = ArgSizes;
10325}
10326
10328 Function *MapperFunc, Value *SrcLocInfo,
10329 Value *MaptypesArg, Value *MapnamesArg,
10331 int64_t DeviceID, unsigned NumOperands) {
10332 if (!updateToLocation(Loc))
10333 return;
10334
10335 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10336 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10337 Value *ArgsBaseGEP =
10338 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10339 {Builder.getInt32(0), Builder.getInt32(0)});
10340 Value *ArgsGEP =
10341 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10342 {Builder.getInt32(0), Builder.getInt32(0)});
10343 Value *ArgSizesGEP =
10344 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10345 {Builder.getInt32(0), Builder.getInt32(0)});
10346 Value *NullPtr =
10347 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10348 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10349 Builder.getInt32(NumOperands),
10350 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10351 MaptypesArg, MapnamesArg, NullPtr});
10352}
10353
10355 TargetDataRTArgs &RTArgs,
10356 TargetDataInfo &Info,
10357 bool ForEndCall) {
10358 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10359 "expected region end call to runtime only when end call is separate");
10360 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10361 auto VoidPtrTy = UnqualPtrTy;
10362 auto VoidPtrPtrTy = UnqualPtrTy;
10363 auto Int64Ty = Type::getInt64Ty(M.getContext());
10364 auto Int64PtrTy = UnqualPtrTy;
10365
10366 if (!Info.NumberOfPtrs) {
10367 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10368 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10369 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10370 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10371 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10372 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10373 return;
10374 }
10375
10376 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10377 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10378 Info.RTArgs.BasePointersArray,
10379 /*Idx0=*/0, /*Idx1=*/0);
10380 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10381 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10382 /*Idx0=*/0,
10383 /*Idx1=*/0);
10384 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10385 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10386 /*Idx0=*/0, /*Idx1=*/0);
10387 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10388 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10389 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10390 : Info.RTArgs.MapTypesArray,
10391 /*Idx0=*/0,
10392 /*Idx1=*/0);
10393
10394 // Only emit the mapper information arrays if debug information is
10395 // requested.
10396 if (!Info.EmitDebug)
10397 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10398 else
10399 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10400 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10401 /*Idx0=*/0,
10402 /*Idx1=*/0);
10403 // If there is no user-defined mapper, set the mapper array to nullptr to
10404 // avoid an unnecessary data privatization
10405 if (!Info.HasMapper)
10406 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10407 else
10408 RTArgs.MappersArray =
10409 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10410}
10411
10413 InsertPointTy CodeGenIP,
10414 MapInfosTy &CombinedInfo,
10415 TargetDataInfo &Info) {
10417 CombinedInfo.NonContigInfo;
10418
10419 // Build an array of struct descriptor_dim and then assign it to
10420 // offload_args.
10421 //
10422 // struct descriptor_dim {
10423 // uint64_t offset;
10424 // uint64_t count;
10425 // uint64_t stride
10426 // };
10427 Type *Int64Ty = Builder.getInt64Ty();
10429 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10430 "struct.descriptor_dim");
10431
10432 enum { OffsetFD = 0, CountFD, StrideFD };
10433 // We need two index variable here since the size of "Dims" is the same as
10434 // the size of Components, however, the size of offset, count, and stride is
10435 // equal to the size of base declaration that is non-contiguous.
10436 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10437 // Skip emitting ir if dimension size is 1 since it cannot be
10438 // non-contiguous.
10439 if (NonContigInfo.Dims[I] == 1)
10440 continue;
10441 Builder.restoreIP(AllocaIP);
10442 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10443 AllocaInst *DimsAddr =
10444 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10445 Builder.restoreIP(CodeGenIP);
10446 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10447 unsigned RevIdx = EE - II - 1;
10448 Value *DimsLVal = Builder.CreateInBoundsGEP(
10449 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10450 // Offset
10451 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10452 Builder.CreateAlignedStore(
10453 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10454 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10455 // Count
10456 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10457 Builder.CreateAlignedStore(
10458 NonContigInfo.Counts[L][RevIdx], CountLVal,
10459 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10460 // Stride
10461 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10462 Builder.CreateAlignedStore(
10463 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10464 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10465 }
10466 // args[I] = &dims
10467 Builder.restoreIP(CodeGenIP);
10468 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10469 DimsAddr, Builder.getPtrTy());
10470 Value *P = Builder.CreateConstInBoundsGEP2_32(
10471 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10472 Info.RTArgs.PointersArray, 0, I);
10473 Builder.CreateAlignedStore(
10474 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10475 ++L;
10476 }
10477}
10478
10479void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10480 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10481 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10482 BasicBlock *ExitBB, bool IsInit) {
10483 StringRef Prefix = IsInit ? ".init" : ".del";
10484
10485 // Evaluate if this is an array section.
10487 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10488 Value *IsArray =
10489 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10490 Value *DeleteBit = Builder.CreateAnd(
10491 MapType,
10492 Builder.getInt64(
10493 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10494 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10495 Value *DeleteCond;
10496 Value *Cond;
10497 if (IsInit) {
10498 // base != begin?
10499 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10500 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10501 DeleteCond = Builder.CreateIsNull(
10502 DeleteBit,
10503 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10504 } else {
10505 Cond = IsArray;
10506 DeleteCond = Builder.CreateIsNotNull(
10507 DeleteBit,
10508 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10509 }
10510 Cond = Builder.CreateAnd(Cond, DeleteCond);
10511 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10512
10513 emitBlock(BodyBB, MapperFn);
10514 // Get the array size by multiplying element size and element number (i.e., \p
10515 // Size).
10516 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10517 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10518 // memory allocation/deletion purpose only.
10519 Value *MapTypeArg = Builder.CreateAnd(
10520 MapType,
10521 Builder.getInt64(
10522 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10523 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10524 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10525 MapTypeArg = Builder.CreateOr(
10526 MapTypeArg,
10527 Builder.getInt64(
10528 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10529 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10530
10531 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10532 // data structure.
10533 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10534 ArraySize, MapTypeArg, MapName};
10536 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10537 OffloadingArgs);
10538}
10539
10542 llvm::Value *BeginArg)>
10543 GenMapInfoCB,
10544 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10545 bool PreserveMemberOfFlags) {
10546 SmallVector<Type *> Params;
10547 Params.emplace_back(Builder.getPtrTy());
10548 Params.emplace_back(Builder.getPtrTy());
10549 Params.emplace_back(Builder.getPtrTy());
10550 Params.emplace_back(Builder.getInt64Ty());
10551 Params.emplace_back(Builder.getInt64Ty());
10552 Params.emplace_back(Builder.getPtrTy());
10553
10554 auto *FnTy =
10555 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10556
10557 SmallString<64> TyStr;
10558 raw_svector_ostream Out(TyStr);
10559 Function *MapperFn =
10561 MapperFn->addFnAttr(Attribute::NoInline);
10562 MapperFn->addFnAttr(Attribute::NoUnwind);
10563 MapperFn->addParamAttr(0, Attribute::NoUndef);
10564 MapperFn->addParamAttr(1, Attribute::NoUndef);
10565 MapperFn->addParamAttr(2, Attribute::NoUndef);
10566 MapperFn->addParamAttr(3, Attribute::NoUndef);
10567 MapperFn->addParamAttr(4, Attribute::NoUndef);
10568 MapperFn->addParamAttr(5, Attribute::NoUndef);
10569
10570 // Start the mapper function code generation.
10571 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10572 auto SavedIP = Builder.saveIP();
10573 Builder.SetInsertPoint(EntryBB);
10574
10575 Value *MapperHandle = MapperFn->getArg(0);
10576 Value *BaseIn = MapperFn->getArg(1);
10577 Value *BeginIn = MapperFn->getArg(2);
10578 Value *Size = MapperFn->getArg(3);
10579 Value *MapType = MapperFn->getArg(4);
10580 Value *MapName = MapperFn->getArg(5);
10581
10582 // Compute the starting and end addresses of array elements.
10583 // Prepare common arguments for array initiation and deletion.
10584 // Convert the size in bytes into the number of array elements.
10585 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10586 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10587 Value *PtrBegin = BeginIn;
10588 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10589
10590 // Emit array initiation if this is an array section and \p MapType indicates
10591 // that memory allocation is required.
10592 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10593 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10594 MapType, MapName, ElementSize, HeadBB,
10595 /*IsInit=*/true);
10596
10597 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10598
10599 // Emit the loop header block.
10600 emitBlock(HeadBB, MapperFn);
10601 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10602 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10603 // Evaluate whether the initial condition is satisfied.
10604 Value *IsEmpty =
10605 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10606 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10607
10608 // Emit the loop body block.
10609 emitBlock(BodyBB, MapperFn);
10610 BasicBlock *LastBB = BodyBB;
10611 PHINode *PtrPHI =
10612 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10613 PtrPHI->addIncoming(PtrBegin, HeadBB);
10614
10615 // Get map clause information. Fill up the arrays with all mapped variables.
10616 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10617 if (!Info)
10618 return Info.takeError();
10619
10620 // Call the runtime API __tgt_mapper_num_components to get the number of
10621 // pre-existing components.
10622 Value *OffloadingArgs[] = {MapperHandle};
10623 Value *PreviousSize = createRuntimeFunctionCall(
10624 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10625 OffloadingArgs);
10626 Value *ShiftedPreviousSize =
10627 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10628
10629 // Fill up the runtime mapper handle for all components.
10630 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10631 Value *CurBaseArg = Info->BasePointers[I];
10632 Value *CurBeginArg = Info->Pointers[I];
10633 Value *CurSizeArg = Info->Sizes[I];
10634 Value *CurNameArg = Info->Names.size()
10635 ? Info->Names[I]
10636 : Constant::getNullValue(Builder.getPtrTy());
10637
10638 Value *OriMapType = Builder.getInt64(
10639 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10640 Info->Types[I]));
10641 auto RawType =
10642 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10643 Info->Types[I]);
10644 constexpr uint64_t MemberOfMask =
10645 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10646 constexpr uint64_t AttachBit =
10647 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10648 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10649
10650 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10651 // current array element (N = __tgt_mapper_num_components() at loop body
10652 // start).
10653 //
10654 // Example 1:
10655 // struct S { int x; int *p; };
10656 //
10657 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10658 // use: S arr[2]; ... map(arr)
10659 // entries per element:
10660 //
10661 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10662 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10663 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10664 //
10665 // Example 2:
10666 // struct S1 { int x; int y; };
10667 // struct S2 { int z; S1 *s1p; };
10668 //
10669 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10670 // s2.s1p->y)
10671 // use: S2 arr[2]; ... map(arr)
10672 // entries per element:
10673 //
10674 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10675 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10676 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10677 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10678 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10679 //
10680 // x/y carry inner MEMBER_OF(2)
10681 // which is shifted by N to become MEMBER_OF(N+2).
10682 //
10683 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10684 // the combined ALLOC entry for the s1p->x..y block, and the individual
10685 // x/y entries that are MEMBER_OF that block, all describe storage
10686 // reached through the attach ptr arr[i].s1p.
10687 //
10688 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10689 // linking them to the parent struct:
10690 //
10691 // * (*) Entries with HasAttachPtr: they represent pointee data that
10692 // occupies a different storage block than the struct being mapped, so
10693 // they are not a member of it. They may still be MEMBER_OF an entry
10694 // within that pointee block, in which case those pre-existing bits are
10695 // shifted -- see (***).
10696 // * (**) ATTACH entries: they are not a member of anything — they just
10697 // link a ptr to its ptee.
10698 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10699 // its pre-shaped entries already carry their final MEMBER_OF bits.
10700 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10701 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10702 // it.
10703 //
10704 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10705 // s1p->x/y entries above), those bits are still shifted by N.
10706 Value *MemberMapType;
10707 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10708 Info->HasAttachPtr[I]) {
10709 if (RawType & MemberOfMask)
10710 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10711 else
10712 MemberMapType = OriMapType;
10713 } else {
10714 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10715 }
10716
10717 // Combine the map type inherited from user-defined mapper with that
10718 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10719 // bits of the \a MapType, which is the input argument of the mapper
10720 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10721 // bits of MemberMapType.
10722 // [OpenMP 5.0], 1.2.6. map-type decay.
10723 // | alloc | to | from | tofrom | release | delete
10724 // ----------------------------------------------------------
10725 // alloc | alloc | alloc | alloc | alloc | release | delete
10726 // to | alloc | to | alloc | to | release | delete
10727 // from | alloc | alloc | from | from | release | delete
10728 // tofrom | alloc | to | from | tofrom | release | delete
10729 Value *LeftToFrom = Builder.CreateAnd(
10730 MapType,
10731 Builder.getInt64(
10732 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10733 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10734 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10735 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10736 BasicBlock *AllocElseBB =
10737 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10738 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10739 BasicBlock *ToElseBB =
10740 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10741 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10742 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10743 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10744 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10745 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10746 emitBlock(AllocBB, MapperFn);
10747 Value *AllocMapType = Builder.CreateAnd(
10748 MemberMapType,
10749 Builder.getInt64(
10750 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10751 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10752 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10753 Builder.CreateBr(EndBB);
10754 emitBlock(AllocElseBB, MapperFn);
10755 Value *IsTo = Builder.CreateICmpEQ(
10756 LeftToFrom,
10757 Builder.getInt64(
10758 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10759 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10760 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10761 // In case of to, clear OMP_MAP_FROM.
10762 emitBlock(ToBB, MapperFn);
10763 Value *ToMapType = Builder.CreateAnd(
10764 MemberMapType,
10765 Builder.getInt64(
10766 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10767 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10768 Builder.CreateBr(EndBB);
10769 emitBlock(ToElseBB, MapperFn);
10770 Value *IsFrom = Builder.CreateICmpEQ(
10771 LeftToFrom,
10772 Builder.getInt64(
10773 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10774 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10775 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10776 // In case of from, clear OMP_MAP_TO.
10777 emitBlock(FromBB, MapperFn);
10778 Value *FromMapType = Builder.CreateAnd(
10779 MemberMapType,
10780 Builder.getInt64(
10781 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10782 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10783 // In case of tofrom, do nothing.
10784 emitBlock(EndBB, MapperFn);
10785 LastBB = EndBB;
10786 PHINode *CurMapType =
10787 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10788 CurMapType->addIncoming(AllocMapType, AllocBB);
10789 CurMapType->addIncoming(ToMapType, ToBB);
10790 CurMapType->addIncoming(FromMapType, FromBB);
10791 CurMapType->addIncoming(MemberMapType, ToElseBB);
10792
10793 // Propagate map-type-modifying bits from the outer map clause to each map
10794 // inserted by the mapper.
10795 //
10796 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10797 // list item from the map clause and to apply the clauses specified in the
10798 // declared mapper to the construct on which the map clause appears...
10799 // If any modifier with the map-type-modifying property appears in the map
10800 // clause then the effect is as if that modifier appears in each map clause
10801 // specified in the declared mapper.
10802 //
10803 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10804 // TODO: PRESENT is not propagated here yet. Doing so requires
10805 // distinguishing pointee entries from the struct's own storage; it is
10806 // handled in a follow-on.
10807 Value *ImportedModifierBits = Builder.CreateAnd(
10808 MapType,
10809 Builder.getInt64(
10810 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10811 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
10812 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
10813 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE)));
10814 Value *CurMapTypeWithModifiers = Builder.CreateOr(
10815 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
10816
10817 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
10818 // reserved for the attach(always) map-type modifier, and other modifier
10819 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
10820 Value *FinalMapType =
10821 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
10822
10823 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
10824 CurSizeArg, FinalMapType, CurNameArg};
10825
10826 auto ChildMapperFn = CustomMapperCB(I);
10827 if (!ChildMapperFn)
10828 return ChildMapperFn.takeError();
10829 if (*ChildMapperFn) {
10830 // Call the corresponding mapper function.
10831 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
10832 ->setDoesNotThrow();
10833 } else {
10834 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10835 // data structure.
10837 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10838 OffloadingArgs);
10839 }
10840 }
10841
10842 // Update the pointer to point to the next element that needs to be mapped,
10843 // and check whether we have mapped all elements.
10844 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
10845 "omp.arraymap.next");
10846 PtrPHI->addIncoming(PtrNext, LastBB);
10847 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
10848 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
10849 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
10850
10851 emitBlock(ExitBB, MapperFn);
10852 // Emit array deletion if this is an array section and \p MapType indicates
10853 // that deletion is required.
10854 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10855 MapType, MapName, ElementSize, DoneBB,
10856 /*IsInit=*/false);
10857
10858 // Emit the function exit block.
10859 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
10860
10861 Builder.CreateRetVoid();
10862 Builder.restoreIP(SavedIP);
10863 return MapperFn;
10864}
10865
10867 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
10868 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
10869 bool IsNonContiguous,
10870 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10871
10872 // Reset the array information.
10873 Info.clearArrayInfo();
10874 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
10875
10876 if (Info.NumberOfPtrs == 0)
10877 return Error::success();
10878
10879 Builder.restoreIP(AllocaIP);
10880 // Detect if we have any capture size requiring runtime evaluation of the
10881 // size so that a constant array could be eventually used.
10882 ArrayType *PointerArrayType =
10883 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
10884
10885 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
10886 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
10887
10888 Info.RTArgs.PointersArray = Builder.CreateAlloca(
10889 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
10890 AllocaInst *MappersArray = Builder.CreateAlloca(
10891 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
10892 Info.RTArgs.MappersArray = MappersArray;
10893
10894 // If we don't have any VLA types or other types that require runtime
10895 // evaluation, we can use a constant array for the map sizes, otherwise we
10896 // need to fill up the arrays as we do for the pointers.
10897 Type *Int64Ty = Builder.getInt64Ty();
10898 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
10899 ConstantInt::get(Int64Ty, 0));
10900 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
10901 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
10902 bool IsNonContigEntry =
10903 IsNonContiguous &&
10904 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10905 CombinedInfo.Types[I] &
10906 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
10907 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
10908 // descriptor_dim records), not the byte size.
10909 if (IsNonContigEntry) {
10910 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
10911 "Index must be in-bounds for NON_CONTIG Dims array");
10912 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
10913 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
10914 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
10915 continue;
10916 }
10917 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
10918 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
10919 ConstSizes[I] = CI;
10920 continue;
10921 }
10922 }
10923 RuntimeSizes.set(I);
10924 }
10925
10926 if (RuntimeSizes.all()) {
10927 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
10928 Info.RTArgs.SizesArray = Builder.CreateAlloca(
10929 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
10930 restoreIPandDebugLoc(Builder, CodeGenIP);
10931 } else {
10932 auto *SizesArrayInit = ConstantArray::get(
10933 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
10934 std::string Name = createPlatformSpecificName({"offload_sizes"});
10935 auto *SizesArrayGbl =
10936 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
10937 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
10938 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
10939
10940 if (!RuntimeSizes.any()) {
10941 Info.RTArgs.SizesArray = SizesArrayGbl;
10942 } else {
10943 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
10944 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
10945 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
10946 AllocaInst *Buffer = Builder.CreateAlloca(
10947 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
10948 Buffer->setAlignment(OffloadSizeAlign);
10949 restoreIPandDebugLoc(Builder, CodeGenIP);
10950 Builder.CreateMemCpy(
10951 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
10952 SizesArrayGbl, OffloadSizeAlign,
10953 Builder.getIntN(
10954 IndexSize,
10955 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
10956
10957 Info.RTArgs.SizesArray = Buffer;
10958 }
10959 restoreIPandDebugLoc(Builder, CodeGenIP);
10960 }
10961
10962 // The map types are always constant so we don't need to generate code to
10963 // fill arrays. Instead, we create an array constant.
10965 for (auto mapFlag : CombinedInfo.Types)
10966 Mapping.push_back(
10967 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10968 mapFlag));
10969 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
10970 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
10971 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
10972
10973 // The information types are only built if provided.
10974 if (!CombinedInfo.Names.empty()) {
10975 auto *MapNamesArrayGbl = createOffloadMapnames(
10976 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
10977 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
10978 Info.EmitDebug = true;
10979 } else {
10980 Info.RTArgs.MapNamesArray =
10982 Info.EmitDebug = false;
10983 }
10984
10985 // If there's a present map type modifier, it must not be applied to the end
10986 // of a region, so generate a separate map type array in that case.
10987 if (Info.separateBeginEndCalls()) {
10988 bool EndMapTypesDiffer = false;
10989 for (uint64_t &Type : Mapping) {
10990 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10991 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
10992 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10993 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10994 EndMapTypesDiffer = true;
10995 }
10996 }
10997 if (EndMapTypesDiffer) {
10998 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
10999 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11000 }
11001 }
11002
11003 PointerType *PtrTy = Builder.getPtrTy();
11004 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11005 Value *BPVal = CombinedInfo.BasePointers[I];
11006 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11007 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11008 0, I);
11009 Builder.CreateAlignedStore(BPVal, BP,
11010 M.getDataLayout().getPrefTypeAlign(PtrTy));
11011
11012 if (Info.requiresDevicePointerInfo()) {
11013 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11014 CodeGenIP = Builder.saveIP();
11015 Builder.restoreIP(AllocaIP);
11016 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11017 restoreIPandDebugLoc(Builder, CodeGenIP);
11018 if (DeviceAddrCB)
11019 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11020 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11021 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11022 if (DeviceAddrCB)
11023 DeviceAddrCB(I, BP);
11024 }
11025 }
11026
11027 Value *PVal = CombinedInfo.Pointers[I];
11028 Value *P = Builder.CreateConstInBoundsGEP2_32(
11029 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11030 I);
11031 // TODO: Check alignment correct.
11032 Builder.CreateAlignedStore(PVal, P,
11033 M.getDataLayout().getPrefTypeAlign(PtrTy));
11034
11035 if (RuntimeSizes.test(I)) {
11036 Value *S = Builder.CreateConstInBoundsGEP2_32(
11037 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11038 /*Idx0=*/0,
11039 /*Idx1=*/I);
11040 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11041 Int64Ty,
11042 /*isSigned=*/true),
11043 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11044 }
11045 // Fill up the mapper array.
11046 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11047 Value *MFunc = ConstantPointerNull::get(PtrTy);
11048
11049 auto CustomMFunc = CustomMapperCB(I);
11050 if (!CustomMFunc)
11051 return CustomMFunc.takeError();
11052 if (*CustomMFunc)
11053 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11054
11055 Value *MAddr = Builder.CreateInBoundsGEP(
11056 PointerArrayType, MappersArray,
11057 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11058 Builder.CreateAlignedStore(
11059 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11060 }
11061
11062 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11063 Info.NumberOfPtrs == 0)
11064 return Error::success();
11065 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11066 return Error::success();
11067}
11068
11070 BasicBlock *CurBB = Builder.GetInsertBlock();
11071
11072 if (!CurBB || CurBB->hasTerminator()) {
11073 // If there is no insert point or the previous block is already
11074 // terminated, don't touch it.
11075 } else {
11076 // Otherwise, create a fall-through branch.
11077 Builder.CreateBr(Target);
11078 }
11079
11080 Builder.ClearInsertionPoint();
11081}
11082
11084 bool IsFinished) {
11085 BasicBlock *CurBB = Builder.GetInsertBlock();
11086
11087 // Fall out of the current block (if necessary).
11088 emitBranch(BB);
11089
11090 if (IsFinished && BB->use_empty()) {
11091 BB->eraseFromParent();
11092 return;
11093 }
11094
11095 // Place the block after the current block, if possible, or else at
11096 // the end of the function.
11097 if (CurBB && CurBB->getParent())
11098 CurFn->insert(std::next(CurBB->getIterator()), BB);
11099 else
11100 CurFn->insert(CurFn->end(), BB);
11101 Builder.SetInsertPoint(BB);
11102}
11103
11105 BodyGenCallbackTy ElseGen,
11106 InsertPointTy AllocaIP,
11107 ArrayRef<BasicBlock *> DeallocBlocks) {
11108 // If the condition constant folds and can be elided, try to avoid emitting
11109 // the condition and the dead arm of the if/else.
11110 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11111 auto CondConstant = CI->getSExtValue();
11112 if (CondConstant)
11113 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11114
11115 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11116 }
11117
11118 Function *CurFn = Builder.GetInsertBlock()->getParent();
11119
11120 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11121 // emit the conditional branch.
11122 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11123 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11124 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11125 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11126 // Emit the 'then' code.
11127 emitBlock(ThenBlock, CurFn);
11128 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11129 return Err;
11130 emitBranch(ContBlock);
11131 // Emit the 'else' code if present.
11132 // There is no need to emit line number for unconditional branch.
11133 emitBlock(ElseBlock, CurFn);
11134 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11135 return Err;
11136 // There is no need to emit line number for unconditional branch.
11137 emitBranch(ContBlock);
11138 // Emit the continuation block for code after the if.
11139 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11140 return Error::success();
11141}
11142
11143bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11144 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11147 "Unexpected Atomic Ordering.");
11148
11149 bool Flush = false;
11151
11152 switch (AK) {
11153 case Read:
11156 FlushAO = AtomicOrdering::Acquire;
11157 Flush = true;
11158 }
11159 break;
11160 case Write:
11161 case Compare:
11162 case Update:
11165 FlushAO = AtomicOrdering::Release;
11166 Flush = true;
11167 }
11168 break;
11169 case Capture:
11170 switch (AO) {
11172 FlushAO = AtomicOrdering::Acquire;
11173 Flush = true;
11174 break;
11176 FlushAO = AtomicOrdering::Release;
11177 Flush = true;
11178 break;
11182 Flush = true;
11183 break;
11184 default:
11185 // do nothing - leave silently.
11186 break;
11187 }
11188 }
11189
11190 if (Flush) {
11191 // Currently Flush RT call still doesn't take memory_ordering, so for when
11192 // that happens, this tries to do the resolution of which atomic ordering
11193 // to use with but issue the flush call
11194 // TODO: pass `FlushAO` after memory ordering support is added
11195 (void)FlushAO;
11196 emitFlush(Loc);
11197 }
11198
11199 // for AO == AtomicOrdering::Monotonic and all other case combinations
11200 // do nothing
11201 return Flush;
11202}
11203
11207 AtomicOrdering AO, InsertPointTy AllocaIP) {
11208 if (!updateToLocation(Loc))
11209 return Loc.IP;
11210
11211 assert(X.Var->getType()->isPointerTy() &&
11212 "OMP Atomic expects a pointer to target memory");
11213 Type *XElemTy = X.ElemTy;
11214 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11215 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11216 "OMP atomic read expected a scalar type");
11217
11218 Value *XRead = nullptr;
11219
11220 if (XElemTy->isIntegerTy()) {
11221 LoadInst *XLD =
11222 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11223 XLD->setAtomic(AO);
11224 XRead = cast<Value>(XLD);
11225 } else if (XElemTy->isStructTy()) {
11226 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11227 // target does not support `atomicrmw` of the size of the struct
11228 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11229 OldVal->setAtomic(AO);
11230 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11231 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11232 OpenMPIRBuilder::AtomicInfo atomicInfo(
11233 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11234 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11235 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11236 XRead = AtomicLoadRes.first;
11237 OldVal->eraseFromParent();
11238 } else {
11239 // We need to perform atomic op as integer
11240 IntegerType *IntCastTy =
11241 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11242 LoadInst *XLoad =
11243 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11244 XLoad->setAtomic(AO);
11245 if (XElemTy->isFloatingPointTy()) {
11246 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11247 } else {
11248 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11249 }
11250 }
11251 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11252 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11253 return Builder.saveIP();
11254}
11255
11258 AtomicOpValue &X, Value *Expr,
11259 AtomicOrdering AO, InsertPointTy AllocaIP) {
11260 if (!updateToLocation(Loc))
11261 return Loc.IP;
11262
11263 assert(X.Var->getType()->isPointerTy() &&
11264 "OMP Atomic expects a pointer to target memory");
11265 Type *XElemTy = X.ElemTy;
11266 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11267 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11268 "OMP atomic write expected a scalar type");
11269
11270 if (XElemTy->isIntegerTy()) {
11271 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11272 XSt->setAtomic(AO);
11273 } else if (XElemTy->isStructTy()) {
11274 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11275 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11276 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11277 OpenMPIRBuilder::AtomicInfo atomicInfo(
11278 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11279 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11280 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11281 OldVal->eraseFromParent();
11282 } else {
11283 // We need to bitcast and perform atomic op as integers
11284 IntegerType *IntCastTy =
11285 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11286 Value *ExprCast =
11287 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11288 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11289 XSt->setAtomic(AO);
11290 }
11291
11292 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11293 return Builder.saveIP();
11294}
11295
11298 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11299 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11300 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11301 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11302 if (!updateToLocation(Loc))
11303 return Loc.IP;
11304
11305 LLVM_DEBUG({
11306 Type *XTy = X.Var->getType();
11307 assert(XTy->isPointerTy() &&
11308 "OMP Atomic expects a pointer to target memory");
11309 Type *XElemTy = X.ElemTy;
11310 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11311 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11312 "OMP atomic update expected a scalar or struct type");
11313 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11314 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&
11315 "OpenMP atomic does not support LT or GT operations");
11316 });
11317
11318 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11319 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11320 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11321 if (!AtomicResult)
11322 return AtomicResult.takeError();
11323 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11324 return Builder.saveIP();
11325}
11326
11327// FIXME: Duplicating AtomicExpand
11328Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11329 AtomicRMWInst::BinOp RMWOp) {
11330 switch (RMWOp) {
11331 case AtomicRMWInst::Add:
11332 return Builder.CreateAdd(Src1, Src2);
11333 case AtomicRMWInst::Sub:
11334 return Builder.CreateSub(Src1, Src2);
11335 case AtomicRMWInst::And:
11336 return Builder.CreateAnd(Src1, Src2);
11338 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11339 case AtomicRMWInst::Or:
11340 return Builder.CreateOr(Src1, Src2);
11341 case AtomicRMWInst::Xor:
11342 return Builder.CreateXor(Src1, Src2);
11347 case AtomicRMWInst::Max:
11348 case AtomicRMWInst::Min:
11361 llvm_unreachable("Unsupported atomic update operation");
11362 }
11363 llvm_unreachable("Unsupported atomic update operation");
11364}
11365
11367 // Loads cannot use Release or AcquireRelease ordering. This load is
11368 // just the initial value for the cmpxchg loop; the cmpxchg itself
11369 // retains the original ordering.
11370 AtomicOrdering LoadAO = AO;
11371
11372 if (AO == AtomicOrdering::Release) {
11374 } else if (AO == AtomicOrdering::AcquireRelease) {
11375 LoadAO = AtomicOrdering::Acquire;
11376 }
11377
11378 return LoadAO;
11379}
11380
11381Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11382 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11384 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11385 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11386 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11387 bool emitRMWOp = false;
11388 switch (RMWOp) {
11389 case AtomicRMWInst::Add:
11390 case AtomicRMWInst::And:
11392 case AtomicRMWInst::Or:
11393 case AtomicRMWInst::Xor:
11395 emitRMWOp = XElemTy;
11396 break;
11397 case AtomicRMWInst::Sub:
11398 emitRMWOp = (IsXBinopExpr && XElemTy);
11399 break;
11400 default:
11401 emitRMWOp = false;
11402 }
11403 emitRMWOp &= XElemTy->isIntegerTy();
11404
11405 std::pair<Value *, Value *> Res;
11406 if (emitRMWOp) {
11407 AtomicRMWInst *RMWInst =
11408 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11409 if (T.isAMDGPU()) {
11410 if (IsIgnoreDenormalMode)
11411 RMWInst->setMetadata("amdgpu.ignore.denormal.mode",
11412 llvm::MDNode::get(Builder.getContext(), {}));
11413 if (!IsFineGrainedMemory)
11414 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11415 llvm::MDNode::get(Builder.getContext(), {}));
11416 if (!IsRemoteMemory)
11417 RMWInst->setMetadata("amdgpu.no.remote.memory",
11418 llvm::MDNode::get(Builder.getContext(), {}));
11419 }
11420 Res.first = RMWInst;
11421 // not needed except in case of postfix captures. Generate anyway for
11422 // consistency with the else part. Will be removed with any DCE pass.
11423 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11424 if (RMWOp == AtomicRMWInst::Xchg)
11425 Res.second = Res.first;
11426 else
11427 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11428 } else if (XElemTy->isStructTy()) {
11429 LoadInst *OldVal =
11430 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11432 OldVal->setAtomic(LoadAO);
11433 const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
11434 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11435
11436 OpenMPIRBuilder::AtomicInfo atomicInfo(
11437 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11438 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11439 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11440 BasicBlock *CurBB = Builder.GetInsertBlock();
11441 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11442 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11443 BasicBlock *ExitBB =
11444 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11445 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11446 X->getName() + ".atomic.cont");
11447 ContBB->getTerminator()->eraseFromParent();
11448 Builder.restoreIP(AllocaIP);
11449 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11450 NewAtomicAddr->setName(X->getName() + "x.new.val");
11451 Builder.SetInsertPoint(ContBB);
11452 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11453 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11454 Value *OldExprVal = PHI;
11455 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11456 if (!CBResult)
11457 return CBResult.takeError();
11458 Value *Upd = *CBResult;
11459 Builder.CreateStore(Upd, NewAtomicAddr);
11462 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11463 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11464 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11465 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11466 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11467 OldVal->eraseFromParent();
11468 Res.first = OldExprVal;
11469 Res.second = Upd;
11470
11471 if (UnreachableInst *ExitTI =
11473 CurBBTI->eraseFromParent();
11474 Builder.SetInsertPoint(ExitBB);
11475 } else {
11476 Builder.SetInsertPoint(ExitTI);
11477 }
11478 } else {
11479 IntegerType *IntCastTy =
11480 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11481 LoadInst *OldVal =
11482 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11484 OldVal->setAtomic(LoadAO);
11485 // CurBB
11486 // | /---\
11487 // ContBB |
11488 // | \---/
11489 // ExitBB
11490 BasicBlock *CurBB = Builder.GetInsertBlock();
11491 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11492 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11493 BasicBlock *ExitBB =
11494 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11495 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11496 X->getName() + ".atomic.cont");
11497 ContBB->getTerminator()->eraseFromParent();
11498 Builder.restoreIP(AllocaIP);
11499 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11500 NewAtomicAddr->setName(X->getName() + "x.new.val");
11501 Builder.SetInsertPoint(ContBB);
11502 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11503 PHI->addIncoming(OldVal, CurBB);
11504 bool IsIntTy = XElemTy->isIntegerTy();
11505 Value *OldExprVal = PHI;
11506 if (!IsIntTy) {
11507 if (XElemTy->isFloatingPointTy()) {
11508 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11509 X->getName() + ".atomic.fltCast");
11510 } else {
11511 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11512 X->getName() + ".atomic.ptrCast");
11513 }
11514 }
11515
11516 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11517 if (!CBResult)
11518 return CBResult.takeError();
11519 Value *Upd = *CBResult;
11520 Builder.CreateStore(Upd, NewAtomicAddr);
11521 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11524 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11525 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11526 Result->setVolatile(VolatileX);
11527 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11528 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11529 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11530 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11531
11532 Res.first = OldExprVal;
11533 Res.second = Upd;
11534
11535 // set Insertion point in exit block
11536 if (UnreachableInst *ExitTI =
11538 CurBBTI->eraseFromParent();
11539 Builder.SetInsertPoint(ExitBB);
11540 } else {
11541 Builder.SetInsertPoint(ExitTI);
11542 }
11543 }
11544
11545 return Res;
11546}
11547
11550 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11551 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11552 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11553 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11554 if (!updateToLocation(Loc))
11555 return Loc.IP;
11556
11557 LLVM_DEBUG({
11558 Type *XTy = X.Var->getType();
11559 assert(XTy->isPointerTy() &&
11560 "OMP Atomic expects a pointer to target memory");
11561 Type *XElemTy = X.ElemTy;
11562 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11563 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11564 "OMP atomic capture expected a scalar or struct type");
11565 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11566 "OpenMP atomic does not support LT or GT operations");
11567 });
11568
11569 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11570 // 'x' is simply atomically rewritten with 'expr'.
11571 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11572 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11573 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11574 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11575 if (!AtomicResult)
11576 return AtomicResult.takeError();
11577 Value *CapturedVal =
11578 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11579 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11580
11581 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11582 return Builder.saveIP();
11583}
11584
11588 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11589 bool IsFailOnly, bool IsWeak) {
11590
11592 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11593 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11594}
11595
11599 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11600 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11601
11602 if (!updateToLocation(Loc))
11603 return Loc.IP;
11604
11605 assert(X.Var->getType()->isPointerTy() &&
11606 "OMP atomic expects a pointer to target memory");
11607 // compare capture
11608 if (V.Var) {
11609 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11610 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11611 }
11612
11613 bool IsInteger = E->getType()->isIntegerTy();
11614
11615 if (Op == OMPAtomicCompareOp::EQ) {
11616 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11617 // R.Var handling.
11618 Value *OldValue = nullptr;
11619 Value *SuccessOrFail = nullptr;
11620
11621 if (!IsInteger && HandleFPNegZero) {
11622 // IEEE 754 special cases for cmpxchg (which is bitwise):
11623 // 1. -0.0 == +0.0 but they have different bit patterns.
11624 // 2. NaN != NaN but identical NaN bit patterns would match.
11625 //
11626 // CurBB:
11627 // %e_int = bitcast E to intN
11628 // %d_int = bitcast D to intN
11629 // %x_curr = load atomic intN, X
11630 // %x_fp = bitcast %x_curr to FP
11631 // %e_is_nan = fcmp uno E, E
11632 // %x_is_nan = fcmp uno %x_fp, %x_fp
11633 // %either_nan = or %e_is_nan, %x_is_nan
11634 // br %either_nan, NaNBB, NotNaNBB
11635 // NaNBB: ; NaN == anything is always false
11636 // br ExitBB
11637 // NotNaNBB:
11638 // %x_is_zero = fcmp oeq %x_fp, 0.0
11639 // %e_is_zero = fcmp oeq E, 0.0
11640 // %both_zero = and %x_is_zero, %e_is_zero
11641 // br %both_zero, ZeroBB, NormalBB
11642 // ZeroBB: ; both ±0.0 → x = d
11643 // cmpxchg X, %x_curr, %d_int
11644 // br ExitBB
11645 // NormalBB: ; original path
11646 // cmpxchg X, %e_int, %d_int
11647 // br ExitBB
11648 // ExitBB:
11649 // phi merge
11650 IntegerType *IntCastTy =
11651 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11652 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11653 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11654
11655 // Load X atomically.
11656 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11657 X.Var->getName() + ".atomic.load");
11659 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11660
11661 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11662 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11663 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11664 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11665 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11666
11667 BasicBlock *CurBB = Builder.GetInsertBlock();
11668 Function *F = CurBB->getParent();
11669 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11670 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11671 BasicBlock *ExitBB =
11672 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11674 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11675 BasicBlock *NotNaNBB = BasicBlock::Create(
11676 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11678 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11679 BasicBlock *NormalBB = BasicBlock::Create(
11680 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11681
11682 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11683 CurBB->getTerminator()->eraseFromParent();
11684 Builder.SetInsertPoint(CurBB);
11685 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11686
11687 // NaNBB: NaN == anything is always false; skip cmpxchg.
11688 Builder.SetInsertPoint(NaNBB);
11689 Builder.CreateBr(ExitBB);
11690
11691 // NotNaNBB: check both X and E for ±0.0.
11692 Builder.SetInsertPoint(NotNaNBB);
11693 Value *XIsZero =
11694 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11695 X.Var->getName() + ".atomic.xiszero");
11696 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11697 "atomic.e.iszero");
11698 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11699 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11700
11701 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11702 Builder.SetInsertPoint(ZeroBB);
11703 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11704 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11705 ResZero->setWeak(IsWeak);
11706 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11707 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11708 Builder.CreateBr(ExitBB);
11709
11710 // NormalBB: original bitwise cmpxchg.
11711 Builder.SetInsertPoint(NormalBB);
11712 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11713 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11714 ResNormal->setWeak(IsWeak);
11715 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11716 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11717 Builder.CreateBr(ExitBB);
11718
11719 // ExitBB: merge results from NaN, Zero, and Normal paths.
11720 Builder.SetInsertPoint(ExitBB, ExitBB->begin());
11721 PHINode *OldIntPHI =
11722 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11723 OldIntPHI->addIncoming(XCurr, NaNBB);
11724 OldIntPHI->addIncoming(OldZero, ZeroBB);
11725 OldIntPHI->addIncoming(OldNormal, NormalBB);
11726 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11727 X.Var->getName() + ".atomic.ok");
11728 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11729 SuccessPHI->addIncoming(OkZero, ZeroBB);
11730 SuccessPHI->addIncoming(OkNormal, NormalBB);
11731
11732 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11733 CurBBTI->eraseFromParent();
11734 Builder.SetInsertPoint(ExitBB);
11735 } else {
11736 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11737 }
11738
11739 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11740 X.Var->getName() + ".atomic.old.fp");
11741 SuccessOrFail = SuccessPHI;
11742 } else {
11743 AtomicCmpXchgInst *Result = nullptr;
11744 if (!IsInteger) {
11745 IntegerType *IntCastTy =
11746 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11747 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11748 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11749 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11750 MaybeAlign(), AO, Failure);
11751 } else {
11752 Result =
11753 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11754 }
11755 Result->setWeak(IsWeak);
11756
11757 if (V.Var) {
11758 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11759 if (!IsInteger)
11760 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11761 assert(OldValue->getType() == V.ElemTy &&
11762 "OldValue and V must be of same type");
11763 if (IsPostfixUpdate) {
11764 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11765 } else {
11766 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11767 if (IsFailOnly) {
11768 BasicBlock *CurBB = Builder.GetInsertBlock();
11769 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11770 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11771 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11772 CurBBTI, X.Var->getName() + ".atomic.exit");
11773 BasicBlock *ContBB = CurBB->splitBasicBlock(
11774 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11775 ContBB->getTerminator()->eraseFromParent();
11776 CurBB->getTerminator()->eraseFromParent();
11777
11778 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11779
11780 Builder.SetInsertPoint(ContBB);
11781 Builder.CreateStore(OldValue, V.Var);
11782 Builder.CreateBr(ExitBB);
11783
11784 if (UnreachableInst *ExitTI =
11786 CurBBTI->eraseFromParent();
11787 Builder.SetInsertPoint(ExitBB);
11788 } else {
11789 Builder.SetInsertPoint(ExitTI);
11790 }
11791 } else {
11792 Value *CapturedValue =
11793 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11794 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11795 }
11796 }
11797 }
11798 // The comparison result has to be stored.
11799 if (R.Var) {
11800 assert(R.Var->getType()->isPointerTy() &&
11801 "r.var must be of pointer type");
11802 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11803
11804 Value *SuccessFailureVal =
11805 Builder.CreateExtractValue(Result, /*Idxs=*/1);
11806 Value *ResultCast =
11807 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
11808 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
11809 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11810 }
11811 }
11812
11813 // For the HandleFPNegZero path, handle V.Var and R.Var using the
11814 // pre-computed OldValue and SuccessOrFail.
11815 if (HandleFPNegZero && !IsInteger) {
11816 if (V.Var) {
11817 assert(OldValue->getType() == V.ElemTy &&
11818 "OldValue and V must be of same type");
11819 if (IsPostfixUpdate) {
11820 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11821 } else {
11822 if (IsFailOnly) {
11823 BasicBlock *CurBB = Builder.GetInsertBlock();
11824 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11825 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11826 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11827 CurBBTI, X.Var->getName() + ".atomic.exit");
11828 BasicBlock *ContBB = CurBB->splitBasicBlock(
11829 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11830 ContBB->getTerminator()->eraseFromParent();
11831 CurBB->getTerminator()->eraseFromParent();
11832
11833 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11834
11835 Builder.SetInsertPoint(ContBB);
11836 Builder.CreateStore(OldValue, V.Var);
11837 Builder.CreateBr(ExitBB);
11838
11839 if (UnreachableInst *ExitTI =
11841 CurBBTI->eraseFromParent();
11842 Builder.SetInsertPoint(ExitBB);
11843 } else {
11844 Builder.SetInsertPoint(ExitTI);
11845 }
11846 } else {
11847 Value *CapturedValue =
11848 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11849 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11850 }
11851 }
11852 }
11853 // The comparison result has to be stored.
11854 if (R.Var) {
11855 assert(R.Var->getType()->isPointerTy() &&
11856 "r.var must be of pointer type");
11857 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11858
11859 Value *ResultCast = R.IsSigned
11860 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
11861 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
11862 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11863 }
11864 }
11865 } else {
11866 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
11867 "Op should be either max or min at this point");
11868 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
11869
11870 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
11871 // Let's take max as example.
11872 // OpenMP form:
11873 // x = x > expr ? expr : x;
11874 // LLVM form:
11875 // *ptr = *ptr > val ? *ptr : val;
11876 // We need to transform to LLVM form.
11877 // x = x <= expr ? x : expr;
11879 if (IsXBinopExpr) {
11880 if (IsInteger) {
11881 if (X.IsSigned)
11882 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
11884 else
11885 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
11887 } else {
11888 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
11890 }
11891 } else {
11892 if (IsInteger) {
11893 if (X.IsSigned)
11894 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
11896 else
11897 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
11899 } else {
11900 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
11902 }
11903 }
11904
11905 AtomicRMWInst *OldValue =
11906 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
11907 if (V.Var) {
11908 Value *CapturedValue = nullptr;
11909 if (IsPostfixUpdate) {
11910 CapturedValue = OldValue;
11911 } else {
11912 CmpInst::Predicate Pred;
11913 switch (NewOp) {
11914 case AtomicRMWInst::Max:
11915 Pred = CmpInst::ICMP_SGT;
11916 break;
11918 Pred = CmpInst::ICMP_UGT;
11919 break;
11921 Pred = CmpInst::FCMP_OGT;
11922 break;
11923 case AtomicRMWInst::Min:
11924 Pred = CmpInst::ICMP_SLT;
11925 break;
11927 Pred = CmpInst::ICMP_ULT;
11928 break;
11930 Pred = CmpInst::FCMP_OLT;
11931 break;
11932 default:
11933 llvm_unreachable("unexpected comparison op");
11934 }
11935 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
11936 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
11937 }
11938 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11939 }
11940 }
11941
11942 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
11943
11944 return Builder.saveIP();
11945}
11946
11949 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
11950 Value *NumTeamsUpper, Value *ThreadLimit,
11951 Value *IfExpr) {
11952 if (!updateToLocation(Loc))
11953 return InsertPointTy();
11954
11955 uint32_t SrcLocStrSize;
11956 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
11957 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
11958 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
11959
11960 // Outer allocation basicblock is the entry block of the current function.
11961 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
11962 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
11963 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
11964 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
11965 }
11966
11967 // The current basic block is split into four basic blocks. After outlining,
11968 // they will be mapped as follows:
11969 // ```
11970 // def current_fn() {
11971 // current_basic_block:
11972 // br label %teams.exit
11973 // teams.exit:
11974 // ; instructions after teams
11975 // }
11976 //
11977 // def outlined_fn() {
11978 // teams.alloca:
11979 // br label %teams.body
11980 // teams.body:
11981 // ; instructions within teams body
11982 // }
11983 // ```
11984 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
11985 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
11986 BasicBlock *AllocaBB =
11987 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
11988
11989 bool SubClausesPresent =
11990 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
11991 // Push num_teams
11992 if (!Config.isTargetDevice() && SubClausesPresent) {
11993 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
11994 "if lowerbound is non-null, then upperbound must also be non-null "
11995 "for bounds on num_teams");
11996
11997 if (NumTeamsUpper == nullptr)
11998 NumTeamsUpper = Builder.getInt32(0);
11999
12000 if (NumTeamsLower == nullptr)
12001 NumTeamsLower = NumTeamsUpper;
12002
12003 if (IfExpr) {
12004 assert(IfExpr->getType()->isIntegerTy() &&
12005 "argument to if clause must be an integer value");
12006
12007 // upper = ifexpr ? upper : 1
12008 if (IfExpr->getType() != Int1)
12009 IfExpr = Builder.CreateICmpNE(IfExpr,
12010 ConstantInt::get(IfExpr->getType(), 0));
12011 NumTeamsUpper = Builder.CreateSelect(
12012 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12013
12014 // lower = ifexpr ? lower : 1
12015 NumTeamsLower = Builder.CreateSelect(
12016 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12017 }
12018
12019 if (ThreadLimit == nullptr)
12020 ThreadLimit = Builder.getInt32(0);
12021
12022 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12023 // truncate or sign extend the passed values to match the int32 parameters.
12024 Value *NumTeamsLowerInt32 =
12025 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12026 Value *NumTeamsUpperInt32 =
12027 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12028 Value *ThreadLimitInt32 =
12029 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12030
12031 Value *ThreadNum = getOrCreateThreadID(Ident);
12032
12034 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12035 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12036 ThreadLimitInt32});
12037 }
12038 // Generate the body of teams.
12039 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12040 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12041 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12042 return Err;
12043
12044 auto OI = std::make_unique<OutlineInfo>();
12045 OI->EntryBB = AllocaBB;
12046 OI->ExitBB = ExitBB;
12047 OI->OuterAllocBB = &OuterAllocaBB;
12048
12049 // Insert fake values for global tid and bound tid.
12051 InsertPointTy OuterAllocaIP(&OuterAllocaBB, OuterAllocaBB.begin());
12052 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12053 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12054 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12055 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12056
12057 auto HostPostOutlineCB = [this, Ident,
12058 ToBeDeleted](Function &OutlinedFn) mutable {
12059 // The stale call instruction will be replaced with a new call instruction
12060 // for runtime call with the outlined function.
12061
12062 assert(OutlinedFn.hasOneUse() &&
12063 "there must be a single user for the outlined function");
12064 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12065 ToBeDeleted.push_back(StaleCI);
12066
12067 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12068 "Outlined function must have two or three arguments only");
12069
12070 bool HasShared = OutlinedFn.arg_size() == 3;
12071
12072 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12073 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12074 if (HasShared)
12075 OutlinedFn.getArg(2)->setName("data");
12076
12077 // Call to the runtime function for teams in the current function.
12078 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12079 "outlined function.");
12080 Builder.SetInsertPoint(StaleCI);
12081 SmallVector<Value *> Args = {
12082 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12083 if (HasShared)
12084 Args.push_back(StaleCI->getArgOperand(2));
12087 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12088 Args);
12089
12090 for (Instruction *I : llvm::reverse(ToBeDeleted))
12091 I->eraseFromParent();
12092 };
12093
12094 if (!Config.isTargetDevice())
12095 OI->PostOutlineCB = HostPostOutlineCB;
12096
12097 addOutlineInfo(std::move(OI));
12098
12099 Builder.SetInsertPoint(ExitBB);
12100
12101 return Builder.saveIP();
12102}
12103
12105 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12106 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12107 if (!updateToLocation(Loc))
12108 return InsertPointTy();
12109
12110 BasicBlock *OuterAllocaBB = OuterAllocIP.getBlock();
12111
12112 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12113 BasicBlock *BodyBB =
12114 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12115 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12116 }
12117 BasicBlock *ExitBB =
12118 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12119 BasicBlock *BodyBB =
12120 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12121 BasicBlock *AllocaBB =
12122 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12123
12124 // Generate the body of distribute clause
12125 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12126 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12127 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12128 return Err;
12129
12130 // When using target we use different runtime functions which require a
12131 // callback.
12132 if (Config.isTargetDevice()) {
12133 auto OI = std::make_unique<OutlineInfo>();
12134 OI->OuterAllocBB = OuterAllocIP.getBlock();
12135 OI->EntryBB = AllocaBB;
12136 OI->ExitBB = ExitBB;
12137 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12138 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12139
12140 addOutlineInfo(std::move(OI));
12141 }
12142 Builder.SetInsertPoint(ExitBB);
12143
12144 return Builder.saveIP();
12145}
12146
12149 std::string VarName) {
12150 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12152 Names.size()),
12153 Names);
12154 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12155 M, MapNamesArrayInit->getType(),
12156 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12157 VarName);
12158 return MapNamesArrayGlobal;
12159}
12160
12161// Create all simple and struct types exposed by the runtime and remember
12162// the llvm::PointerTypes of them for easy access later.
12163void OpenMPIRBuilder::initializeTypes(Module &M) {
12164 LLVMContext &Ctx = M.getContext();
12165 StructType *T;
12166 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12167 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12168#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12169#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12170 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12171 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12172#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12173 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12174 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12175#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12176 T = StructType::getTypeByName(Ctx, StructName); \
12177 if (!T) \
12178 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12179 VarName = T; \
12180 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12181#include "llvm/Frontend/OpenMP/OMPKinds.def"
12182}
12183
12186 SmallVectorImpl<BasicBlock *> &BlockVector) {
12188 BlockSet.insert(EntryBB);
12189 BlockSet.insert(ExitBB);
12190
12191 Worklist.push_back(EntryBB);
12192 while (!Worklist.empty()) {
12193 BasicBlock *BB = Worklist.pop_back_val();
12194 BlockVector.push_back(BB);
12195 for (BasicBlock *SuccBB : successors(BB))
12196 if (BlockSet.insert(SuccBB).second)
12197 Worklist.push_back(SuccBB);
12198 }
12199}
12200
12201std::unique_ptr<CodeExtractor>
12203 bool ArgsInZeroAddressSpace,
12204 Twine Suffix) {
12205 return std::make_unique<CodeExtractor>(
12206 Blocks, /* DominatorTree */ nullptr,
12207 /* AggregateArgs */ true,
12208 /* BlockFrequencyInfo */ nullptr,
12209 /* BranchProbabilityInfo */ nullptr,
12210 /* AssumptionCache */ nullptr,
12211 /* AllowVarArgs */ true,
12212 /* AllowAlloca */ true,
12213 /* AllocationBlock*/ OuterAllocBB,
12214 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12215 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12216}
12217
12218std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12219 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12220 return std::make_unique<DeviceSharedMemCodeExtractor>(
12221 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12222 /* AggregateArgs */ true,
12223 /* BlockFrequencyInfo */ nullptr,
12224 /* BranchProbabilityInfo */ nullptr,
12225 /* AssumptionCache */ nullptr,
12226 /* AllowVarArgs */ true,
12227 /* AllowAlloca */ true,
12228 /* AllocationBlock*/ OuterAllocBB,
12229 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12231 : OuterDeallocBBs,
12232 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12233}
12234
12236 uint64_t Size, int32_t Flags,
12238 StringRef Name) {
12239 if (!Config.isGPU()) {
12242 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12243 return;
12244 }
12245 // TODO: Add support for global variables on the device after declare target
12246 // support.
12247 Function *Fn = dyn_cast<Function>(Addr);
12248 if (!Fn)
12249 return;
12250
12251 // Add a function attribute for the kernel.
12252 Fn->addFnAttr("kernel");
12253 if (T.isAMDGCN())
12254 Fn->addFnAttr("uniform-work-group-size");
12255 Fn->addFnAttr(Attribute::MustProgress);
12256}
12257
12258// We only generate metadata for function that contain target regions.
12261
12262 // If there are no entries, we don't need to do anything.
12263 if (OffloadInfoManager.empty())
12264 return;
12265
12266 LLVMContext &C = M.getContext();
12269 16>
12270 OrderedEntries(OffloadInfoManager.size());
12271
12272 // Auxiliary methods to create metadata values and strings.
12273 auto &&GetMDInt = [this](unsigned V) {
12274 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12275 };
12276
12277 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12278
12279 // Create the offloading info metadata node.
12280 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12281 auto &&TargetRegionMetadataEmitter =
12282 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12283 const TargetRegionEntryInfo &EntryInfo,
12285 // Generate metadata for target regions. Each entry of this metadata
12286 // contains:
12287 // - Entry 0 -> Kind of this type of metadata (0).
12288 // - Entry 1 -> Device ID of the file where the entry was identified.
12289 // - Entry 2 -> File ID of the file where the entry was identified.
12290 // - Entry 3 -> Mangled name of the function where the entry was
12291 // identified.
12292 // - Entry 4 -> Line in the file where the entry was identified.
12293 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12294 // - Entry 6 -> Order the entry was created.
12295 // The first element of the metadata node is the kind.
12296 Metadata *Ops[] = {
12297 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12298 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12299 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12300 GetMDInt(E.getOrder())};
12301
12302 // Save this entry in the right position of the ordered entries array.
12303 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12304
12305 // Add metadata to the named metadata node.
12306 MD->addOperand(MDNode::get(C, Ops));
12307 };
12308
12309 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12310
12311 // Create function that emits metadata for each device global variable entry;
12312 auto &&DeviceGlobalVarMetadataEmitter =
12313 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12314 StringRef MangledName,
12316 // Generate metadata for global variables. Each entry of this metadata
12317 // contains:
12318 // - Entry 0 -> Kind of this type of metadata (1).
12319 // - Entry 1 -> Mangled name of the variable.
12320 // - Entry 2 -> Declare target kind.
12321 // - Entry 3 -> Order the entry was created.
12322 // The first element of the metadata node is the kind.
12323 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12324 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12325
12326 // Save this entry in the right position of the ordered entries array.
12327 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12328 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12329
12330 // Add metadata to the named metadata node.
12331 MD->addOperand(MDNode::get(C, Ops));
12332 };
12333
12334 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12335 DeviceGlobalVarMetadataEmitter);
12336
12337 for (const auto &E : OrderedEntries) {
12338 assert(E.first && "All ordered entries must exist!");
12339 if (const auto *CE =
12341 E.first)) {
12342 if (!CE->getID() || !CE->getAddress()) {
12343 // Do not blame the entry if the parent funtion is not emitted.
12344 TargetRegionEntryInfo EntryInfo = E.second;
12345 StringRef FnName = EntryInfo.ParentName;
12346 if (!M.getNamedValue(FnName))
12347 continue;
12348 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12349 continue;
12350 }
12351 createOffloadEntry(CE->getID(), CE->getAddress(),
12352 /*Size=*/0, CE->getFlags(),
12354 } else if (const auto *CE = dyn_cast<
12356 E.first)) {
12359 CE->getFlags());
12360 switch (Flags) {
12363 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12364 continue;
12365 if (!CE->getAddress()) {
12366 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12367 continue;
12368 }
12369 // The vaiable has no definition - no need to add the entry.
12370 if (CE->getVarSize() == 0)
12371 continue;
12372 break;
12374 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12375 (!Config.isTargetDevice() && CE->getAddress())) &&
12376 "Declaret target link address is set.");
12377 if (Config.isTargetDevice())
12378 continue;
12379 if (!CE->getAddress()) {
12381 continue;
12382 }
12383 break;
12386 if (!CE->getAddress()) {
12387 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12388 continue;
12389 }
12390 break;
12391 default:
12392 break;
12393 }
12394
12395 // Hidden or internal symbols on the device are not externally visible.
12396 // We should not attempt to register them by creating an offloading
12397 // entry. Indirect variables are handled separately on the device.
12398 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12399 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12400 (Flags !=
12402 Flags != OffloadEntriesInfoManager::
12403 OMPTargetGlobalVarEntryIndirectVTable))
12404 continue;
12405
12406 // Indirect globals need to use a special name that doesn't match the name
12407 // of the associated host global.
12409 Flags ==
12411 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12412 Flags, CE->getLinkage(), CE->getVarName());
12413 else
12414 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12415 Flags, CE->getLinkage());
12416
12417 } else {
12418 llvm_unreachable("Unsupported entry kind.");
12419 }
12420 }
12421
12422 // Emit requires directive globals to a special entry so the runtime can
12423 // register them when the device image is loaded.
12424 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12425 // entries should be redesigned to better suit this use-case.
12426 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12430 ".requires", /*Size=*/0,
12432 Config.getRequiresFlags());
12433}
12434
12437 unsigned FileID, unsigned Line, unsigned Count) {
12438 raw_svector_ostream OS(Name);
12439 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12440 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12441 if (Count)
12442 OS << "_" << Count;
12443}
12444
12446 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12447 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12449 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12450 EntryInfo.Line, NewCount);
12451}
12452
12455 vfs::FileSystem &VFS,
12456 StringRef ParentName) {
12457 sys::fs::UniqueID ID(0xdeadf17e, 0);
12458 auto FileIDInfo = CallBack();
12459 uint64_t FileID = 0;
12460 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12461 ID = Status->getUniqueID();
12462 FileID = Status->getUniqueID().getFile();
12463 } else {
12464 // If the inode ID could not be determined, create a hash value
12465 // the current file name and use that as an ID.
12466 FileID = hash_value(std::get<0>(FileIDInfo));
12467 }
12468
12469 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12470 std::get<1>(FileIDInfo));
12471}
12472
12474 unsigned Offset = 0;
12475 for (uint64_t Remain =
12476 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12478 !(Remain & 1); Remain = Remain >> 1)
12479 Offset++;
12480 return Offset;
12481}
12482
12485 // Rotate by getFlagMemberOffset() bits.
12486 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12487 << getFlagMemberOffset());
12488}
12489
12492 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12493 // If the entry is PTR_AND_OBJ but has not been marked with the special
12494 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12495 // marked as MEMBER_OF.
12496 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12498 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12501 return;
12502
12503 // Entries with ATTACH are not members-of anything. They are handled
12504 // separately by the runtime after other maps have been handled.
12505 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12507 return;
12508
12509 // Reset the placeholder value to prepare the flag for the assignment of the
12510 // proper MEMBER_OF value.
12511 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12512 Flags |= MemberOfFlag;
12513}
12514
12518 bool IsDeclaration, bool IsExternallyVisible,
12519 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12520 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12521 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12522 std::function<Constant *()> GlobalInitializer,
12523 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12524 // TODO: convert this to utilise the IRBuilder Config rather than
12525 // a passed down argument.
12526 if (OpenMPSIMD)
12527 return nullptr;
12528
12531 CaptureClause ==
12533 Config.hasRequiresUnifiedSharedMemory())) {
12534 SmallString<64> PtrName;
12535 {
12536 raw_svector_ostream OS(PtrName);
12537 OS << MangledName;
12538 if (!IsExternallyVisible)
12539 OS << format("_%x", EntryInfo.FileID);
12540 OS << "_decl_tgt_ref_ptr";
12541 }
12542
12543 Value *Ptr = M.getNamedValue(PtrName);
12544
12545 if (!Ptr) {
12546 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12547 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12548
12549 auto *GV = cast<GlobalVariable>(Ptr);
12550 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12551
12552 if (!Config.isTargetDevice()) {
12553 if (GlobalInitializer)
12554 GV->setInitializer(GlobalInitializer());
12555 else
12556 GV->setInitializer(GlobalValue);
12557 }
12558
12560 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12561 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12562 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12563 }
12564
12565 return cast<Constant>(Ptr);
12566 }
12567
12568 return nullptr;
12569}
12570
12574 bool IsDeclaration, bool IsExternallyVisible,
12575 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12576 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12577 std::vector<Triple> TargetTriple,
12578 std::function<Constant *()> GlobalInitializer,
12579 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12580 Constant *Addr) {
12582 (TargetTriple.empty() && !Config.isTargetDevice()))
12583 return;
12584
12586 StringRef VarName;
12587 int64_t VarSize;
12589
12591 CaptureClause ==
12593 !Config.hasRequiresUnifiedSharedMemory()) {
12595 VarName = MangledName;
12596 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12597
12598 if (!IsDeclaration)
12599 VarSize = divideCeil(
12600 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12601 else
12602 VarSize = 0;
12603 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12604
12605 // This is a workaround carried over from Clang which prevents undesired
12606 // optimisation of internal variables.
12607 if (Config.isTargetDevice() &&
12608 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12609 // Do not create a "ref-variable" if the original is not also available
12610 // on the host.
12611 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12612 return;
12613
12614 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12615
12616 if (!M.getNamedValue(RefName)) {
12617 Constant *AddrRef =
12618 getOrCreateInternalVariable(Addr->getType(), RefName);
12619 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12620 GvAddrRef->setConstant(true);
12621 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12622 GvAddrRef->setInitializer(Addr);
12623 GeneratedRefs.push_back(GvAddrRef);
12624 }
12625 }
12626 } else {
12629 else
12631
12632 if (Config.isTargetDevice()) {
12633 VarName = (Addr) ? Addr->getName() : "";
12634 Addr = nullptr;
12635 } else {
12637 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12638 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12639 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12640 VarName = (Addr) ? Addr->getName() : "";
12641 }
12642 VarSize = M.getDataLayout().getPointerSize();
12644 }
12645
12646 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12647 Flags, Linkage);
12648}
12649
12650/// Loads all the offload entries information from the host IR
12651/// metadata.
12653 // If we are in target mode, load the metadata from the host IR. This code has
12654 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12655
12656 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12657 if (!MD)
12658 return;
12659
12660 for (MDNode *MN : MD->operands()) {
12661 auto &&GetMDInt = [MN](unsigned Idx) {
12662 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12663 return cast<ConstantInt>(V->getValue())->getZExtValue();
12664 };
12665
12666 auto &&GetMDString = [MN](unsigned Idx) {
12667 auto *V = cast<MDString>(MN->getOperand(Idx));
12668 return V->getString();
12669 };
12670
12671 switch (GetMDInt(0)) {
12672 default:
12673 llvm_unreachable("Unexpected metadata!");
12674 break;
12675 case OffloadEntriesInfoManager::OffloadEntryInfo::
12676 OffloadingEntryInfoTargetRegion: {
12677 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12678 /*DeviceID=*/GetMDInt(1),
12679 /*FileID=*/GetMDInt(2),
12680 /*Line=*/GetMDInt(4),
12681 /*Count=*/GetMDInt(5));
12682 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12683 /*Order=*/GetMDInt(6));
12684 break;
12685 }
12686 case OffloadEntriesInfoManager::OffloadEntryInfo::
12687 OffloadingEntryInfoDeviceGlobalVar:
12688 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12689 /*MangledName=*/GetMDString(1),
12691 /*Flags=*/GetMDInt(2)),
12692 /*Order=*/GetMDInt(3));
12693 break;
12694 }
12695 }
12696}
12697
12699 StringRef HostFilePath) {
12700 if (HostFilePath.empty())
12701 return;
12702
12703 auto Buf = VFS.getBufferForFile(HostFilePath);
12704 if (std::error_code Err = Buf.getError()) {
12705 report_fatal_error(("error opening host file from host file path inside of "
12706 "OpenMPIRBuilder: " +
12707 Err.message())
12708 .c_str());
12709 }
12710
12711 LLVMContext Ctx;
12713 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12714 if (std::error_code Err = M.getError()) {
12716 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12717 .c_str());
12718 }
12719
12720 loadOffloadInfoMetadata(*M.get());
12721}
12722
12725 llvm::StringRef Name) {
12726 Builder.restoreIP(Loc.IP);
12727
12728 BasicBlock *CurBB = Builder.GetInsertBlock();
12729 assert(CurBB &&
12730 "expected a valid insertion block for creating an iterator loop");
12731 Function *F = CurBB->getParent();
12732
12733 InsertPointTy SplitIP = Builder.saveIP();
12734 if (SplitIP.getPoint() == CurBB->end())
12735 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12736 SplitIP = InsertPointTy(CurBB, Terminator->getIterator());
12737
12738 BasicBlock *ContBB =
12739 splitBB(SplitIP, /*CreateBranch=*/false,
12740 Builder.getCurrentDebugLocation(), "omp.it.cont");
12741
12742 CanonicalLoopInfo *CLI =
12743 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12744 /*PreInsertBefore=*/ContBB,
12745 /*PostInsertBefore=*/ContBB, Name);
12746
12747 // Enter loop from original block.
12748 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12749
12750 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12751 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12752 T->eraseFromParent();
12753
12754 InsertPointTy BodyIP = CLI->getBodyIP();
12755 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12756 return Err;
12757
12758 // Body must either fallthrough to the latch or branch directly to it.
12759 if (Instruction *BodyTerminator = CLI->getBody()->getTerminatorOrNull()) {
12760 auto *BodyBr = dyn_cast<UncondBrInst>(BodyTerminator);
12761 if (!BodyBr || BodyBr->getSuccessor() != CLI->getLatch()) {
12763 "iterator bodygen must terminate the canonical body with an "
12764 "unconditional branch to the loop latch",
12766 }
12767 } else {
12768 // Ensure we end the loop body by jumping to the latch.
12769 Builder.SetInsertPoint(CLI->getBody());
12770 Builder.CreateBr(CLI->getLatch());
12771 }
12772
12773 // Link After -> ContBB
12774 Builder.SetInsertPoint(CLI->getAfter(), CLI->getAfter()->begin());
12775 if (!CLI->getAfter()->hasTerminator())
12776 Builder.CreateBr(ContBB);
12777
12778 return InsertPointTy{ContBB, ContBB->begin()};
12779}
12780
12781/// Mangle the parameter part of the vector function name according to
12782/// their OpenMP classification. The mangling function is defined in
12783/// section 4.5 of the AAVFABI(2021Q1).
12784static std::string mangleVectorParameters(
12786 SmallString<256> Buffer;
12787 llvm::raw_svector_ostream Out(Buffer);
12788 for (const auto &ParamAttr : ParamAttrs) {
12789 switch (ParamAttr.Kind) {
12791 Out << 'l';
12792 break;
12794 Out << 'R';
12795 break;
12797 Out << 'U';
12798 break;
12800 Out << 'L';
12801 break;
12803 Out << 'u';
12804 break;
12806 Out << 'v';
12807 break;
12808 }
12809 if (ParamAttr.HasVarStride)
12810 Out << "s" << ParamAttr.StrideOrArg;
12811 else if (ParamAttr.Kind ==
12813 ParamAttr.Kind ==
12815 ParamAttr.Kind ==
12817 ParamAttr.Kind ==
12819 // Don't print the step value if it is not present or if it is
12820 // equal to 1.
12821 if (ParamAttr.StrideOrArg < 0)
12822 Out << 'n' << -ParamAttr.StrideOrArg;
12823 else if (ParamAttr.StrideOrArg != 1)
12824 Out << ParamAttr.StrideOrArg;
12825 }
12826
12827 if (!!ParamAttr.Alignment)
12828 Out << 'a' << ParamAttr.Alignment;
12829 }
12830
12831 return std::string(Out.str());
12832}
12833
12835 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
12837 struct ISADataTy {
12838 char ISA;
12839 unsigned VecRegSize;
12840 };
12841 ISADataTy ISAData[] = {
12842 {'b', 128}, // SSE
12843 {'c', 256}, // AVX
12844 {'d', 256}, // AVX2
12845 {'e', 512}, // AVX512
12846 };
12848 switch (Branch) {
12850 Masked.push_back('N');
12851 Masked.push_back('M');
12852 break;
12854 Masked.push_back('N');
12855 break;
12857 Masked.push_back('M');
12858 break;
12859 }
12860 for (char Mask : Masked) {
12861 for (const ISADataTy &Data : ISAData) {
12863 llvm::raw_svector_ostream Out(Buffer);
12864 Out << "_ZGV" << Data.ISA << Mask;
12865 if (!VLENVal) {
12866 assert(NumElts && "Non-zero simdlen/cdtsize expected");
12867 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
12868 } else {
12869 Out << VLENVal;
12870 }
12871 Out << mangleVectorParameters(ParamAttrs);
12872 Out << '_' << Fn->getName();
12873 Fn->addFnAttr(Out.str());
12874 }
12875 }
12876}
12877
12878// Function used to add the attribute. The parameter `VLEN` is templated to
12879// allow the use of `x` when targeting scalable functions for SVE.
12880template <typename T>
12881static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
12882 char ISA, StringRef ParSeq,
12883 StringRef MangledName, bool OutputBecomesInput,
12884 llvm::Function *Fn) {
12885 SmallString<256> Buffer;
12886 llvm::raw_svector_ostream Out(Buffer);
12887 Out << Prefix << ISA << LMask << VLEN;
12888 if (OutputBecomesInput)
12889 Out << 'v';
12890 Out << ParSeq << '_' << MangledName;
12891 Fn->addFnAttr(Out.str());
12892}
12893
12894// Helper function to generate the Advanced SIMD names depending on the value
12895// of the NDS when simdlen is not present.
12896static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
12897 StringRef Prefix, char ISA,
12898 StringRef ParSeq, StringRef MangledName,
12899 bool OutputBecomesInput,
12900 llvm::Function *Fn) {
12901 switch (NDS) {
12902 case 8:
12903 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12904 OutputBecomesInput, Fn);
12905 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
12906 OutputBecomesInput, Fn);
12907 break;
12908 case 16:
12909 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
12910 OutputBecomesInput, Fn);
12911 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12912 OutputBecomesInput, Fn);
12913 break;
12914 case 32:
12915 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
12916 OutputBecomesInput, Fn);
12917 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
12918 OutputBecomesInput, Fn);
12919 break;
12920 case 64:
12921 case 128:
12922 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
12923 OutputBecomesInput, Fn);
12924 break;
12925 default:
12926 llvm_unreachable("Scalar type is too wide.");
12927 }
12928}
12929
12930/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
12932 llvm::Function *Fn, unsigned UserVLEN,
12934 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
12935 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
12936
12937 // Sort out parameter sequence.
12938 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
12939 StringRef Prefix = "_ZGV";
12940 StringRef MangledName = Fn->getName();
12941
12942 // Generate simdlen from user input (if any).
12943 if (UserVLEN) {
12944 if (ISA == 's') {
12945 // SVE generates only a masked function.
12946 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
12947 OutputBecomesInput, Fn);
12948 return;
12949 }
12950
12951 switch (Branch) {
12953 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
12954 OutputBecomesInput, Fn);
12955 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
12956 OutputBecomesInput, Fn);
12957 break;
12959 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
12960 OutputBecomesInput, Fn);
12961 break;
12963 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
12964 OutputBecomesInput, Fn);
12965 break;
12966 }
12967 return;
12968 }
12969
12970 if (ISA == 's') {
12971 // SVE, section 3.4.1, item 1.
12972 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
12973 OutputBecomesInput, Fn);
12974 return;
12975 }
12976
12977 switch (Branch) {
12979 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
12980 MangledName, OutputBecomesInput, Fn);
12981 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
12982 MangledName, OutputBecomesInput, Fn);
12983 break;
12985 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
12986 MangledName, OutputBecomesInput, Fn);
12987 break;
12989 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
12990 MangledName, OutputBecomesInput, Fn);
12991 break;
12992 }
12993}
12994
12995//===----------------------------------------------------------------------===//
12996// OffloadEntriesInfoManager
12997//===----------------------------------------------------------------------===//
12998
13000 return OffloadEntriesTargetRegion.empty() &&
13001 OffloadEntriesDeviceGlobalVar.empty();
13002}
13003
13004unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13005 const TargetRegionEntryInfo &EntryInfo) const {
13006 auto It = OffloadEntriesTargetRegionCount.find(
13007 getTargetRegionEntryCountKey(EntryInfo));
13008 if (It == OffloadEntriesTargetRegionCount.end())
13009 return 0;
13010 return It->second;
13011}
13012
13013void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13014 const TargetRegionEntryInfo &EntryInfo) {
13015 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13016 EntryInfo.Count + 1;
13017}
13018
13019/// Initialize target region entry.
13021 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13022 OffloadEntriesTargetRegion[EntryInfo] =
13023 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13025 ++OffloadingEntriesNum;
13026}
13027
13029 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13031 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13032
13033 // Update the EntryInfo with the next available count for this location.
13034 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13035
13036 // If we are emitting code for a target, the entry is already initialized,
13037 // only has to be registered.
13038 if (OMPBuilder->Config.isTargetDevice()) {
13039 // This could happen if the device compilation is invoked standalone.
13040 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13041 return;
13042 }
13043 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13044 Entry.setAddress(Addr);
13045 Entry.setID(ID);
13046 Entry.setFlags(Flags);
13047 } else {
13049 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13050 return;
13051 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13052 "Target region entry already registered!");
13053 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13054 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13055 ++OffloadingEntriesNum;
13056 }
13057 incrementTargetRegionEntryInfoCount(EntryInfo);
13058}
13059
13061 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13062
13063 // Update the EntryInfo with the next available count for this location.
13064 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13065
13066 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13067 if (It == OffloadEntriesTargetRegion.end()) {
13068 return false;
13069 }
13070 // Fail if this entry is already registered.
13071 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13072 return false;
13073 return true;
13074}
13075
13077 const OffloadTargetRegionEntryInfoActTy &Action) {
13078 // Scan all target region entries and perform the provided action.
13079 for (const auto &It : OffloadEntriesTargetRegion) {
13080 Action(It.first, It.second);
13081 }
13082}
13083
13085 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13086 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13087 ++OffloadingEntriesNum;
13088}
13089
13091 StringRef VarName, Constant *Addr, int64_t VarSize,
13093 if (OMPBuilder->Config.isTargetDevice()) {
13094 // This could happen if the device compilation is invoked standalone.
13095 if (!hasDeviceGlobalVarEntryInfo(VarName))
13096 return;
13097 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13098 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13099 if (Entry.getVarSize() == 0) {
13100 Entry.setVarSize(VarSize);
13101 Entry.setLinkage(Linkage);
13102 }
13103 return;
13104 }
13105 Entry.setVarSize(VarSize);
13106 Entry.setLinkage(Linkage);
13107 Entry.setAddress(Addr);
13108 } else {
13109 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13110 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13111 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13112 "Entry not initialized!");
13113 if (Entry.getVarSize() == 0) {
13114 Entry.setVarSize(VarSize);
13115 Entry.setLinkage(Linkage);
13116 }
13117 return;
13118 }
13120 Flags ==
13122 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13123 Addr, VarSize, Flags, Linkage,
13124 VarName.str());
13125 else
13126 OffloadEntriesDeviceGlobalVar.try_emplace(
13127 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13128 ++OffloadingEntriesNum;
13129 }
13130}
13131
13134 // Scan all target region entries and perform the provided action.
13135 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13136 Action(E.getKey(), E.getValue());
13137}
13138
13139//===----------------------------------------------------------------------===//
13140// CanonicalLoopInfo
13141//===----------------------------------------------------------------------===//
13142
13143void CanonicalLoopInfo::collectControlBlocks(
13145 // We only count those BBs as control block for which we do not need to
13146 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13147 // flow. For consistency, this also means we do not add the Body block, which
13148 // is just the entry to the body code.
13149 BBs.reserve(BBs.size() + 6);
13150 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13151}
13152
13154 assert(isValid() && "Requires a valid canonical loop");
13155 for (BasicBlock *Pred : predecessors(Header)) {
13156 if (Pred != Latch)
13157 return Pred;
13158 }
13159 llvm_unreachable("Missing preheader");
13160}
13161
13162void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13163 assert(isValid() && "Requires a valid canonical loop");
13164
13165 Instruction *CmpI = &getCond()->front();
13166 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13167 CmpI->setOperand(1, TripCount);
13168
13169#ifndef NDEBUG
13170 assertOK();
13171#endif
13172}
13173
13174void CanonicalLoopInfo::mapIndVar(
13175 llvm::function_ref<Value *(Instruction *)> Updater) {
13176 assert(isValid() && "Requires a valid canonical loop");
13177
13178 Instruction *OldIV = getIndVar();
13179
13180 // Record all uses excluding those introduced by the updater. Uses by the
13181 // CanonicalLoopInfo itself to keep track of the number of iterations are
13182 // excluded.
13183 SmallVector<Use *> ReplacableUses;
13184 for (Use &U : OldIV->uses()) {
13185 auto *User = dyn_cast<Instruction>(U.getUser());
13186 if (!User)
13187 continue;
13188 if (User->getParent() == getCond())
13189 continue;
13190 if (User->getParent() == getLatch())
13191 continue;
13192 ReplacableUses.push_back(&U);
13193 }
13194
13195 // Run the updater that may introduce new uses
13196 Value *NewIV = Updater(OldIV);
13197
13198 // Replace the old uses with the value returned by the updater.
13199 for (Use *U : ReplacableUses)
13200 U->set(NewIV);
13201
13202#ifndef NDEBUG
13203 assertOK();
13204#endif
13205}
13206
13208#ifndef NDEBUG
13209 // No constraints if this object currently does not describe a loop.
13210 if (!isValid())
13211 return;
13212
13213 BasicBlock *Preheader = getPreheader();
13214 BasicBlock *Body = getBody();
13215 BasicBlock *After = getAfter();
13216
13217 // Verify standard control-flow we use for OpenMP loops.
13218 assert(Preheader);
13219 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13220 "Preheader must terminate with unconditional branch");
13221 assert(Preheader->getSingleSuccessor() == Header &&
13222 "Preheader must jump to header");
13223
13224 assert(Header);
13225 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13226 "Header must terminate with unconditional branch");
13227 assert(Header->getSingleSuccessor() == Cond &&
13228 "Header must jump to exiting block");
13229
13230 assert(Cond);
13231 assert(Cond->getSinglePredecessor() == Header &&
13232 "Exiting block only reachable from header");
13233
13234 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13235 "Exiting block must terminate with conditional branch");
13236 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13237 "Exiting block's first successor jump to the body");
13238 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13239 "Exiting block's second successor must exit the loop");
13240
13241 assert(Body);
13242 assert(Body->getSinglePredecessor() == Cond &&
13243 "Body only reachable from exiting block");
13244 assert(!isa<PHINode>(Body->front()));
13245
13246 assert(Latch);
13247 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13248 "Latch must terminate with unconditional branch");
13249 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13250 // TODO: To support simple redirecting of the end of the body code that has
13251 // multiple; introduce another auxiliary basic block like preheader and after.
13252 assert(Latch->getSinglePredecessor() != nullptr);
13253 assert(!isa<PHINode>(Latch->front()));
13254
13255 assert(Exit);
13256 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13257 "Exit block must terminate with unconditional branch");
13258 assert(Exit->getSingleSuccessor() == After &&
13259 "Exit block must jump to after block");
13260
13261 assert(After);
13262 assert(After->getSinglePredecessor() == Exit &&
13263 "After block only reachable from exit block");
13264 assert(After->empty() || !isa<PHINode>(After->front()));
13265
13266 Instruction *IndVar = getIndVar();
13267 assert(IndVar && "Canonical induction variable not found?");
13268 assert(isa<IntegerType>(IndVar->getType()) &&
13269 "Induction variable must be an integer");
13270 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13271 "Induction variable must be a PHI in the loop header");
13272 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13273 assert(
13274 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13275 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13276
13277 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13278 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13279 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13280 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13281 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13282 ->isOne());
13283
13284 Value *TripCount = getTripCount();
13285 assert(TripCount && "Loop trip count not found?");
13286 assert(IndVar->getType() == TripCount->getType() &&
13287 "Trip count and induction variable must have the same type");
13288
13289 auto *CmpI = cast<CmpInst>(&Cond->front());
13290 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13291 "Exit condition must be a signed less-than comparison");
13292 assert(CmpI->getOperand(0) == IndVar &&
13293 "Exit condition must compare the induction variable");
13294 assert(CmpI->getOperand(1) == TripCount &&
13295 "Exit condition must compare with the trip count");
13296#endif
13297}
13298
13300 Header = nullptr;
13301 Cond = nullptr;
13302 Latch = nullptr;
13303 Exit = nullptr;
13304}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
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
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:474
iterator begin()
Instruction iterator methods.
Definition BasicBlock.h:461
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:477
bool hasTerminator() const LLVM_READONLY
Returns whether the block has a terminator.
Definition BasicBlock.h:232
bool empty() const
Definition BasicBlock.h:483
const Instruction & back() const
Definition BasicBlock.h:486
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:484
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:479
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:388
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:659
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:786
Argument * arg_iterator
Definition Function.h:73
bool empty() const
Definition Function.h:836
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:830
arg_iterator arg_begin()
Definition Function.h:845
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:732
size_t arg_size() const
Definition Function.h:878
Type * getReturnType() const
Returns the type of the ret val.
Definition Function.h:216
iterator end()
Definition Function.h:832
void setCallingConv(CallingConv::ID CC)
Definition Function.h:276
Argument * getArg(unsigned i) const
Definition Function.h:863
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:587
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 CanonicalLoopInfo * createLoopSkeleton(DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore, BasicBlock *PostInsertBefore, const Twine &Name={})
Create the control flow structure of a canonical OpenMP loop.
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 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)
Emit the user-defined mapper function.
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 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 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:993
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:390
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:94
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
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.
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 StrictBlocksAndThreads
True if the kernel strictly requires the number of blocks and threads above to run.
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 * MaxThreads
'parallel' construct 'num_threads' clause value, if present and it is an SPMD kernel.
Value * LoopTripCount
Total number of iterations of the SPMD or Generic-SPMD kernel or null if it is a generic 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),...