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 wrapper over IRBuilderBase::restoreIP that also restores the current
166/// debug location to the last instruction in the specified basic block if the
167/// insert point points to the end of the block.
170 Builder.restoreIP(IP);
171 llvm::BasicBlock *BB = Builder.GetInsertBlock();
172 llvm::BasicBlock::iterator I = Builder.GetInsertPoint();
173 if (!BB->empty() && I == BB->end())
174 Builder.SetCurrentDebugLocation(BB->back().getStableDebugLoc());
175}
176
177static bool hasGridValue(const Triple &T) {
178 return T.isAMDGPU() || T.isNVPTX() || T.isSPIRV();
179}
180
181static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {
182 if (T.isAMDGPU()) {
183 StringRef Features =
184 Kernel->getFnAttribute("target-features").getValueAsString();
185 if (Features.count("+wavefrontsize64"))
188 }
189 if (T.isNVPTX())
191 if (T.isSPIRV())
193 llvm_unreachable("No grid value available for this architecture!");
194}
195
196/// Determine which scheduling algorithm to use, determined from schedule clause
197/// arguments.
198static OMPScheduleType
199getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,
200 bool HasSimdModifier, bool HasDistScheduleChunks) {
201 // Currently, the default schedule it static.
202 switch (ClauseKind) {
203 case OMP_SCHEDULE_Default:
204 case OMP_SCHEDULE_Static:
205 return HasChunks ? OMPScheduleType::BaseStaticChunked
206 : OMPScheduleType::BaseStatic;
207 case OMP_SCHEDULE_Dynamic:
208 return OMPScheduleType::BaseDynamicChunked;
209 case OMP_SCHEDULE_Guided:
210 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd
211 : OMPScheduleType::BaseGuidedChunked;
212 case OMP_SCHEDULE_Auto:
214 case OMP_SCHEDULE_Runtime:
215 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd
216 : OMPScheduleType::BaseRuntime;
217 case OMP_SCHEDULE_Distribute:
218 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked
219 : OMPScheduleType::BaseDistribute;
220 }
221 llvm_unreachable("unhandled schedule clause argument");
222}
223
224/// Adds ordering modifier flags to schedule type.
225static OMPScheduleType
227 bool HasOrderedClause) {
228 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==
229 OMPScheduleType::None &&
230 "Must not have ordering nor monotonicity flags already set");
231
232 OMPScheduleType OrderingModifier = HasOrderedClause
233 ? OMPScheduleType::ModifierOrdered
234 : OMPScheduleType::ModifierUnordered;
235 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;
236
237 // Unsupported combinations
238 if (OrderingScheduleType ==
239 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))
240 return OMPScheduleType::OrderedGuidedChunked;
241 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |
242 OMPScheduleType::ModifierOrdered))
243 return OMPScheduleType::OrderedRuntime;
244
245 return OrderingScheduleType;
246}
247
248/// Adds monotonicity modifier flags to schedule type.
249static OMPScheduleType
251 bool HasSimdModifier, bool HasMonotonic,
252 bool HasNonmonotonic, bool HasOrderedClause) {
253 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==
254 OMPScheduleType::None &&
255 "Must not have monotonicity flags already set");
256 assert((!HasMonotonic || !HasNonmonotonic) &&
257 "Monotonic and Nonmonotonic are contradicting each other");
258
259 if (HasMonotonic) {
260 return ScheduleType | OMPScheduleType::ModifierMonotonic;
261 } else if (HasNonmonotonic) {
262 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
263 } else {
264 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.
265 // If the static schedule kind is specified or if the ordered clause is
266 // specified, and if the nonmonotonic modifier is not specified, the
267 // effect is as if the monotonic modifier is specified. Otherwise, unless
268 // the monotonic modifier is specified, the effect is as if the
269 // nonmonotonic modifier is specified.
270 OMPScheduleType BaseScheduleType =
271 ScheduleType & ~OMPScheduleType::ModifierMask;
272 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||
273 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||
274 HasOrderedClause) {
275 // The monotonic is used by default in openmp runtime library, so no need
276 // to set it.
277 return ScheduleType;
278 } else {
279 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
280 }
281 }
282}
283
284/// Determine the schedule type using schedule and ordering clause arguments.
285static OMPScheduleType
286computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,
287 bool HasSimdModifier, bool HasMonotonicModifier,
288 bool HasNonmonotonicModifier, bool HasOrderedClause,
289 bool HasDistScheduleChunks) {
291 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);
292 OMPScheduleType OrderedSchedule =
293 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);
295 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,
296 HasNonmonotonicModifier, HasOrderedClause);
297
299 return Result;
300}
301
302/// Given a function, if it represents the entry point of a target kernel, this
303/// returns the execution mode flags associated with that kernel.
304static std::optional<omp::OMPTgtExecModeFlags>
306 CallInst *TargetInitCall = nullptr;
307 for (Instruction &Inst : Kernel.getEntryBlock()) {
308 if (auto *Call = dyn_cast<CallInst>(&Inst)) {
309 if (Call->getCalledFunction()->getName() == "__kmpc_target_init") {
310 TargetInitCall = Call;
311 break;
312 }
313 }
314 }
315
316 if (!TargetInitCall)
317 return std::nullopt;
318
319 // Get the kernel mode information from the global variable associated to the
320 // first argument to the call to __kmpc_target_init. Refer to
321 // createTargetInit() to see how this is initialized.
322 Value *InitOperand = TargetInitCall->getArgOperand(0);
323 GlobalVariable *KernelEnv = nullptr;
324 if (auto *Cast = dyn_cast<ConstantExpr>(InitOperand))
325 KernelEnv = cast<GlobalVariable>(Cast->getOperand(0));
326 else
327 KernelEnv = cast<GlobalVariable>(InitOperand);
328 auto *KernelEnvInit = cast<ConstantStruct>(KernelEnv->getInitializer());
329 auto *ConfigEnv = cast<ConstantStruct>(KernelEnvInit->getOperand(0));
330 auto *KernelMode = cast<ConstantInt>(ConfigEnv->getOperand(2));
331 return static_cast<OMPTgtExecModeFlags>(KernelMode->getZExtValue());
332}
333
334static bool isGenericKernel(Function &Fn) {
335 std::optional<omp::OMPTgtExecModeFlags> ExecMode =
337 return !ExecMode || (*ExecMode & OMP_TGT_EXEC_MODE_GENERIC);
338}
339
340/// Make \p Source branch to \p Target.
341///
342/// Handles two situations:
343/// * \p Source already has an unconditional branch.
344/// * \p Source is a degenerate block (no terminator because the BB is
345/// the current head of the IR construction).
347 if (Instruction *Term = Source->getTerminatorOrNull()) {
348 auto *Br = cast<UncondBrInst>(Term);
349 BasicBlock *Succ = Br->getSuccessor();
350 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);
351 Br->setSuccessor(Target);
352 return;
353 }
354
355 auto *NewBr = UncondBrInst::Create(Target, Source);
356 NewBr->setDebugLoc(DL);
357}
358
360 bool CreateBranch, DebugLoc DL) {
361 assert(New->getFirstInsertionPt() == New->begin() &&
362 "Target BB must not have PHI nodes");
363
364 // Move instructions to new block.
365 BasicBlock *Old = IP.getBlock();
366 // If the `Old` block is empty then there are no instructions to move. But in
367 // the new debug scheme, it could have trailing debug records which will be
368 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2
369 // reasons:
370 // 1. If `New` is also empty, `BasicBlock::splice` crashes.
371 // 2. Even if `New` is not empty, the rationale to move those records to `New`
372 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function
373 // assumes that `Old` is optimized out and is going away. This is not the case
374 // here. The `Old` block is still being used e.g. a branch instruction is
375 // added to it later in this function.
376 // So we call `BasicBlock::splice` only when `Old` is not empty.
377 if (!Old->empty())
378 New->splice(New->begin(), Old, IP.getPoint(), Old->end());
379
380 if (CreateBranch) {
381 auto *NewBr = UncondBrInst::Create(New, Old);
382 NewBr->setDebugLoc(DL);
383 }
384}
385
386void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {
387 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
388 BasicBlock *Old = Builder.GetInsertBlock();
389
390 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);
391 if (CreateBranch)
392 Builder.SetInsertPoint(Old->getTerminator());
393 else
394 Builder.SetInsertPoint(Old);
395
396 // SetInsertPoint also updates the Builder's debug location, but we want to
397 // keep the one the Builder was configured to use.
398 Builder.SetCurrentDebugLocation(DebugLoc);
399}
400
402 DebugLoc DL, llvm::Twine Name) {
403 BasicBlock *Old = IP.getBlock();
405 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,
406 Old->getParent(), Old->getNextNode());
407 spliceBB(IP, New, CreateBranch, DL);
408 New->replaceSuccessorsPhiUsesWith(Old, New);
409 return New;
410}
411
412BasicBlock *llvm::splitBB(IRBuilderBase &Builder, bool CreateBranch,
413 llvm::Twine Name) {
414 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
415 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
416 if (CreateBranch)
417 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
418 else
419 Builder.SetInsertPoint(Builder.GetInsertBlock());
420 // SetInsertPoint also updates the Builder's debug location, but we want to
421 // keep the one the Builder was configured to use.
422 Builder.SetCurrentDebugLocation(DebugLoc);
423 return New;
424}
425
426BasicBlock *llvm::splitBB(IRBuilder<> &Builder, bool CreateBranch,
427 llvm::Twine Name) {
428 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
429 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
430 if (CreateBranch)
431 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
432 else
433 Builder.SetInsertPoint(Builder.GetInsertBlock());
434 // SetInsertPoint also updates the Builder's debug location, but we want to
435 // keep the one the Builder was configured to use.
436 Builder.SetCurrentDebugLocation(DebugLoc);
437 return New;
438}
439
441 llvm::Twine Suffix) {
442 BasicBlock *Old = Builder.GetInsertBlock();
443 return splitBB(Builder, CreateBranch, Old->getName() + Suffix);
444}
445
446// This function creates a fake integer value and a fake use for the integer
447// value. It returns the fake value created. This is useful in modeling the
448// extra arguments to the outlined functions.
450 OpenMPIRBuilder::InsertPointTy OuterAllocaIP,
452 OpenMPIRBuilder::InsertPointTy InnerAllocaIP,
453 const Twine &Name = "", bool AsPtr = true,
454 bool Is64Bit = false) {
455 Builder.restoreIP(OuterAllocaIP);
456 IntegerType *IntTy = Is64Bit ? Builder.getInt64Ty() : Builder.getInt32Ty();
457 Instruction *FakeVal;
458 AllocaInst *FakeValAddr =
459 Builder.CreateAlloca(IntTy, nullptr, Name + ".addr");
460 ToBeDeleted.push_back(FakeValAddr);
461
462 if (AsPtr) {
463 FakeVal = FakeValAddr;
464 } else {
465 FakeVal = Builder.CreateLoad(IntTy, FakeValAddr, Name + ".val");
466 ToBeDeleted.push_back(FakeVal);
467 }
468
469 // Generate a fake use of this value
470 Builder.restoreIP(InnerAllocaIP);
471 Instruction *UseFakeVal;
472 if (AsPtr) {
473 UseFakeVal = Builder.CreateLoad(IntTy, FakeVal, Name + ".use");
474 } else {
475 UseFakeVal = cast<BinaryOperator>(Builder.CreateAdd(
476 FakeVal, Is64Bit ? Builder.getInt64(10) : Builder.getInt32(10)));
477 }
478 ToBeDeleted.push_back(UseFakeVal);
479 return FakeVal;
480}
481
482//===----------------------------------------------------------------------===//
483// OpenMPIRBuilderConfig
484//===----------------------------------------------------------------------===//
485
486namespace {
488/// Values for bit flags for marking which requires clauses have been used.
489enum OpenMPOffloadingRequiresDirFlags {
490 /// flag undefined.
491 OMP_REQ_UNDEFINED = 0x000,
492 /// no requires directive present.
493 OMP_REQ_NONE = 0x001,
494 /// reverse_offload clause.
495 OMP_REQ_REVERSE_OFFLOAD = 0x002,
496 /// unified_address clause.
497 OMP_REQ_UNIFIED_ADDRESS = 0x004,
498 /// unified_shared_memory clause.
499 OMP_REQ_UNIFIED_SHARED_MEMORY = 0x008,
500 /// dynamic_allocators clause.
501 OMP_REQ_DYNAMIC_ALLOCATORS = 0x010,
502 LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)
503};
504
505class OMPCodeExtractor : public CodeExtractor {
506public:
507 OMPCodeExtractor(OpenMPIRBuilder &OMPBuilder, ArrayRef<BasicBlock *> BBs,
508 DominatorTree *DT = nullptr, bool AggregateArgs = false,
509 BlockFrequencyInfo *BFI = nullptr,
510 BranchProbabilityInfo *BPI = nullptr,
511 AssumptionCache *AC = nullptr, bool AllowVarArgs = false,
512 bool AllowAlloca = false,
513 BasicBlock *AllocationBlock = nullptr,
514 ArrayRef<BasicBlock *> DeallocationBlocks = {},
515 std::string Suffix = "", bool ArgsInZeroAddressSpace = false)
516 : CodeExtractor(BBs, DT, AggregateArgs, BFI, BPI, AC, AllowVarArgs,
517 AllowAlloca, AllocationBlock, DeallocationBlocks, Suffix,
518 ArgsInZeroAddressSpace),
519 OMPBuilder(OMPBuilder) {}
520
521 virtual ~OMPCodeExtractor() = default;
522
523protected:
524 OpenMPIRBuilder &OMPBuilder;
525};
526
527class DeviceSharedMemCodeExtractor : public OMPCodeExtractor {
528public:
529 using OMPCodeExtractor::OMPCodeExtractor;
530 virtual ~DeviceSharedMemCodeExtractor() = default;
531
532protected:
533 virtual Instruction *
534 allocateVar(IRBuilder<>::InsertPoint AllocaIP, Type *VarType,
535 const Twine &Name = Twine(""),
536 AddrSpaceCastInst **CastedAlloc = nullptr) override {
537 return OMPBuilder.createOMPAllocShared(AllocaIP, VarType, Name);
538 }
539
540 virtual Instruction *deallocateVar(IRBuilder<>::InsertPoint DeallocIP,
541 Value *Var, Type *VarType) override {
542 return OMPBuilder.createOMPFreeShared(DeallocIP, Var, VarType);
543 }
544};
545
546/// Helper storing information about regions to outline using device shared
547/// memory for intermediate allocations.
548struct DeviceSharedMemOutlineInfo : public OpenMPIRBuilder::OutlineInfo {
549 OpenMPIRBuilder &OMPBuilder;
550
551 DeviceSharedMemOutlineInfo(OpenMPIRBuilder &OMPBuilder)
552 : OMPBuilder(OMPBuilder) {}
553 virtual ~DeviceSharedMemOutlineInfo() = default;
554
555 virtual std::unique_ptr<CodeExtractor>
556 createCodeExtractor(ArrayRef<BasicBlock *> Blocks,
557 bool ArgsInZeroAddressSpace,
558 Twine Suffix = Twine("")) override;
559};
560
561} // anonymous namespace
562
564 : RequiresFlags(OMP_REQ_UNDEFINED) {}
565
568 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,
569 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)
572 RequiresFlags(OMP_REQ_UNDEFINED) {
573 if (HasRequiresReverseOffload)
574 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
575 if (HasRequiresUnifiedAddress)
576 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
577 if (HasRequiresUnifiedSharedMemory)
578 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
579 if (HasRequiresDynamicAllocators)
580 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
581}
582
584 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;
585}
586
588 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;
589}
590
592 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;
593}
594
596 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;
597}
598
600 return hasRequiresFlags() ? RequiresFlags
601 : static_cast<int64_t>(OMP_REQ_NONE);
602}
603
605 if (Value)
606 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
607 else
608 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;
609}
610
612 if (Value)
613 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
614 else
615 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;
616}
617
619 if (Value)
620 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
621 else
622 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;
623}
624
626 if (Value)
627 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
628 else
629 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;
630}
631
632//===----------------------------------------------------------------------===//
633// OpenMPIRBuilder
634//===----------------------------------------------------------------------===//
635
638 SmallVector<Value *> &ArgsVector) {
640 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);
641 auto Int32Ty = Type::getInt32Ty(Builder.getContext());
642 constexpr size_t MaxDim = 3;
643 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));
644
645 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);
646
647 Value *DynCGroupMemFallbackFlag =
648 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));
649 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);
650
651 Value *StrictFlag = Builder.getInt64(KernelArgs.StrictBlocksAndThreads);
652 StrictFlag = Builder.CreateShl(StrictFlag, 6);
653
654 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
655 Flags = Builder.CreateOr(Flags, StrictFlag);
656
657 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
658
659 Value *NumTeams3D =
660 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
661 Value *NumThreads3D =
662 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
663 for (unsigned I :
664 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
665 NumTeams3D =
666 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
667 for (unsigned I :
668 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
669 NumThreads3D =
670 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
671
672 ArgsVector = {Version,
673 PointerNum,
674 KernelArgs.RTArgs.BasePointersArray,
675 KernelArgs.RTArgs.PointersArray,
676 KernelArgs.RTArgs.SizesArray,
677 KernelArgs.RTArgs.MapTypesArray,
678 KernelArgs.RTArgs.MapNamesArray,
679 KernelArgs.RTArgs.MappersArray,
680 KernelArgs.NumIterations,
681 Flags,
682 NumTeams3D,
683 NumThreads3D,
684 KernelArgs.DynCGroupMem};
685}
686
688 LLVMContext &Ctx = Fn.getContext();
689
690 // Get the function's current attributes.
691 auto Attrs = Fn.getAttributes();
692 auto FnAttrs = Attrs.getFnAttrs();
693 auto RetAttrs = Attrs.getRetAttrs();
695 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
696 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
697
698 // Add AS to FnAS while taking special care with integer extensions.
699 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
700 bool Param = true) -> void {
701 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
702 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
703 if (HasSignExt || HasZeroExt) {
704 assert(AS.getNumAttributes() == 1 &&
705 "Currently not handling extension attr combined with others.");
706 if (Param) {
707 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
708 FnAS = FnAS.addAttribute(Ctx, AK);
709 } else if (auto AK =
710 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
711 FnAS = FnAS.addAttribute(Ctx, AK);
712 } else {
713 FnAS = FnAS.addAttributes(Ctx, AS);
714 }
715 };
716
717#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
718#include "llvm/Frontend/OpenMP/OMPKinds.def"
719
720 // Add attributes to the function declaration.
721 switch (FnID) {
722#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
723 case Enum: \
724 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
725 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
726 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
727 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
728 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
729 break;
730#include "llvm/Frontend/OpenMP/OMPKinds.def"
731 default:
732 // Attributes are optional.
733 break;
734 }
735}
736
739 FunctionType *FnTy = nullptr;
740 Function *Fn = nullptr;
741
742 // Try to find the declation in the module first.
743 switch (FnID) {
744#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
745 case Enum: \
746 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
747 IsVarArg); \
748 Fn = M.getFunction(Str); \
749 break;
750#include "llvm/Frontend/OpenMP/OMPKinds.def"
751 }
752
753 if (!Fn) {
754 // Create a new declaration if we need one.
755 switch (FnID) {
756#define OMP_RTL(Enum, Str, ...) \
757 case Enum: \
758 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
759 break;
760#include "llvm/Frontend/OpenMP/OMPKinds.def"
761 }
762 Fn->setCallingConv(Config.getRuntimeCC());
763 // Add information if the runtime function takes a callback function
764 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
765 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
766 LLVMContext &Ctx = Fn->getContext();
767 MDBuilder MDB(Ctx);
768 // Annotate the callback behavior of the runtime function:
769 // - The callback callee is argument number 2 (microtask).
770 // - The first two arguments of the callback callee are unknown (-1).
771 // - All variadic arguments to the runtime function are passed to the
772 // callback callee.
773 Fn->addMetadata(
774 LLVMContext::MD_callback,
776 2, {-1, -1}, /* VarArgsArePassed */ true)}));
777 }
778 }
779
780 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
781 << " with type " << *Fn->getFunctionType() << "\n");
782 addAttributes(FnID, *Fn);
783
784 } else {
785 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
786 << " with type " << *Fn->getFunctionType() << "\n");
787 }
788
789 assert(Fn && "Failed to create OpenMP runtime function");
790
791 return {FnTy, Fn};
792}
793
796 if (!FiniBB) {
797 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
799 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
800 Builder.SetInsertPoint(FiniBB);
801 // FiniCB adds the branch to the exit stub.
802 if (Error Err = FiniCB(Builder.saveIP()))
803 return Err;
804 }
805 return FiniBB;
806}
807
809 BasicBlock *OtherFiniBB) {
810 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
811 if (!FiniBB) {
812 FiniBB = OtherFiniBB;
813
814 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
815 if (Error Err = FiniCB(Builder.saveIP()))
816 return Err;
817
818 return Error::success();
819 }
820
821 // Move instructions from FiniBB to the start of OtherFiniBB.
822 auto EndIt = FiniBB->end();
823 if (FiniBB->size() >= 1)
824 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
825 EndIt = Prev;
826 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
827 EndIt);
828
829 FiniBB->replaceAllUsesWith(OtherFiniBB);
830 FiniBB->eraseFromParent();
831 FiniBB = OtherFiniBB;
832 return Error::success();
833}
834
837 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
838 assert(Fn && "Failed to create OpenMP runtime function pointer");
839 return Fn;
840}
841
844 StringRef Name) {
845 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
846 Call->setCallingConv(Config.getRuntimeCC());
847 return Call;
848}
849
850void OpenMPIRBuilder::initialize() { initializeTypes(M); }
851
854 BasicBlock &EntryBlock = Function->getEntryBlock();
855 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
856
857 // Loop over blocks looking for constant allocas, skipping the entry block
858 // as any allocas there are already in the desired location.
859 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
860 Block++) {
861 for (auto Inst = Block->getReverseIterator()->begin();
862 Inst != Block->getReverseIterator()->end();) {
864 Inst++;
866 continue;
867 AllocaInst->moveBeforePreserving(MoveLocInst);
868 } else {
869 Inst++;
870 }
871 }
872 }
873}
874
877
878 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
879 // TODO: For now, we support simple static allocations, we might need to
880 // move non-static ones as well. However, this will need further analysis to
881 // move the lenght arguments as well.
883 };
884
885 for (llvm::Instruction &Inst : Block)
887 if (ShouldHoistAlloca(*AllocaInst))
888 AllocasToMove.push_back(AllocaInst);
889
890 auto InsertPoint =
891 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
892
893 for (llvm::Instruction *AllocaInst : AllocasToMove)
895}
896
898 PostDominatorTree PostDomTree(*Func);
899 for (llvm::BasicBlock &BB : *Func)
900 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
902}
903
905 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
907 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
908 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
909 // Skip functions that have not finalized yet; may happen with nested
910 // function generation.
911 if (Fn && OI->getFunction() != Fn) {
912 DeferredOutlines.push_back(std::move(OI));
913 continue;
914 }
915
916 ParallelRegionBlockSet.clear();
917 Blocks.clear();
918 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
919
920 Function *OuterFn = OI->getFunction();
921 CodeExtractorAnalysisCache CEAC(*OuterFn);
922 // If we generate code for the target device, we need to allocate
923 // struct for aggregate params in the device default alloca address space.
924 // OpenMP runtime requires that the params of the extracted functions are
925 // passed as zero address space pointers. This flag ensures that
926 // CodeExtractor generates correct code for extracted functions
927 // which are used by OpenMP runtime.
928 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
929 std::unique_ptr<CodeExtractor> Extractor =
930 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
931
932 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
933 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
934 << " Exit: " << OI->ExitBB->getName() << "\n");
935 assert(Extractor->isEligible() &&
936 "Expected OpenMP outlining to be possible!");
937
938 for (auto *V : OI->ExcludeArgsFromAggregate)
939 Extractor->excludeArgFromAggregate(V);
940
941 Function *OutlinedFn =
942 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
943
944 // Forward target-cpu, target-features attributes to the outlined function.
945 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
946 if (TargetCpuAttr.isStringAttribute())
947 OutlinedFn->addFnAttr(TargetCpuAttr);
948
949 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
950 if (TargetFeaturesAttr.isStringAttribute())
951 OutlinedFn->addFnAttr(TargetFeaturesAttr);
952
953 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
954 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
955 assert(OutlinedFn->getReturnType()->isVoidTy() &&
956 "OpenMP outlined functions should not return a value!");
957
958 // For compability with the clang CG we move the outlined function after the
959 // one with the parallel region.
960 OutlinedFn->removeFromParent();
961 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
962
963 // Remove the artificial entry introduced by the extractor right away, we
964 // made our own entry block after all.
965 {
966 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
967 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
968 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
969 // Move instructions from the to-be-deleted ArtificialEntry to the entry
970 // basic block of the parallel region. CodeExtractor generates
971 // instructions to unwrap the aggregate argument and may sink
972 // allocas/bitcasts for values that are solely used in the outlined region
973 // and do not escape.
974 assert(!ArtificialEntry.empty() &&
975 "Expected instructions to add in the outlined region entry");
976 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
977 End = ArtificialEntry.rend();
978 It != End;) {
979 Instruction &I = *It;
980 It++;
981
982 if (I.isTerminator()) {
983 // Absorb any debug value that terminator may have
984 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
985 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
986 continue;
987 }
988
989 I.moveBeforePreserving(*OI->EntryBB,
990 OI->EntryBB->getFirstInsertionPt());
991 }
992
993 OI->EntryBB->moveBefore(&ArtificialEntry);
994 ArtificialEntry.eraseFromParent();
995 }
996 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
997 assert(OutlinedFn && OutlinedFn->hasNUses(1));
998
999 // Run a user callback, e.g. to add attributes.
1000 if (OI->PostOutlineCB)
1001 OI->PostOutlineCB(*OutlinedFn);
1002
1003 if (OI->FixUpNonEntryAllocas)
1005 }
1006
1007 // Remove work items that have been completed.
1008 OutlineInfos = std::move(DeferredOutlines);
1009
1010 // The createTarget functions embeds user written code into
1011 // the target region which may inject allocas which need to
1012 // be moved to the entry block of our target or risk malformed
1013 // optimisations by later passes, this is only relevant for
1014 // the device pass which appears to be a little more delicate
1015 // when it comes to optimisations (however, we do not block on
1016 // that here, it's up to the inserter to the list to do so).
1017 // This notbaly has to occur after the OutlinedInfo candidates
1018 // have been extracted so we have an end product that will not
1019 // be implicitly adversely affected by any raises unless
1020 // intentionally appended to the list.
1021 // NOTE: This only does so for ConstantData, it could be extended
1022 // to ConstantExpr's with further effort, however, they should
1023 // largely be folded when they get here. Extending it to runtime
1024 // defined/read+writeable allocation sizes would be non-trivial
1025 // (need to factor in movement of any stores to variables the
1026 // allocation size depends on, as well as the usual loads,
1027 // otherwise it'll yield the wrong result after movement) and
1028 // likely be more suitable as an LLVM optimisation pass.
1031
1032 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1033 [](EmitMetadataErrorKind Kind,
1034 const TargetRegionEntryInfo &EntryInfo) -> void {
1035 errs() << "Error of kind: " << Kind
1036 << " when emitting offload entries and metadata during "
1037 "OMPIRBuilder finalization \n";
1038 };
1039
1040 if (!OffloadInfoManager.empty())
1042
1043 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1044 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1045 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1046 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1047 }
1048
1049 IsFinalized = true;
1050}
1051
1052bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1053
1055 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1056}
1057
1059 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1060 auto *GV =
1061 new GlobalVariable(M, I32Ty,
1062 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1063 ConstantInt::get(I32Ty, Value), Name);
1064 GV->setVisibility(GlobalValue::HiddenVisibility);
1065
1066 return GV;
1067}
1068
1070 if (List.empty())
1071 return;
1072
1073 // Convert List to what ConstantArray needs.
1075 UsedArray.resize(List.size());
1076 for (unsigned I = 0, E = List.size(); I != E; ++I)
1078 cast<Constant>(&*List[I]), Builder.getPtrTy());
1079
1080 if (UsedArray.empty())
1081 return;
1082 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1083
1084 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1085 ConstantArray::get(ATy, UsedArray), Name);
1086
1087 GV->setSection("llvm.metadata");
1088}
1089
1092 OMPTgtExecModeFlags Mode) {
1093 auto *Int8Ty = Builder.getInt8Ty();
1094 auto *GVMode = new GlobalVariable(
1095 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1096 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1097 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1098 return GVMode;
1099}
1100
1102 uint32_t SrcLocStrSize,
1103 IdentFlag LocFlags,
1104 unsigned Reserve2Flags) {
1105 // Enable "C-mode".
1106 LocFlags |= OMP_IDENT_FLAG_KMPC;
1107
1108 Constant *&Ident =
1109 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1110 if (!Ident) {
1111 Constant *I32Null = ConstantInt::getNullValue(Int32);
1112 Constant *IdentData[] = {I32Null,
1113 ConstantInt::get(Int32, uint32_t(LocFlags)),
1114 ConstantInt::get(Int32, Reserve2Flags),
1115 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1116
1117 size_t SrcLocStrArgIdx = 4;
1118 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1120 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1121 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1122 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1123 Constant *Initializer =
1124 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1125
1126 // Look for existing encoding of the location + flags, not needed but
1127 // minimizes the difference to the existing solution while we transition.
1128 for (GlobalVariable &GV : M.globals())
1129 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1130 if (GV.getInitializer() == Initializer)
1131 Ident = &GV;
1132
1133 if (!Ident) {
1134 auto *GV = new GlobalVariable(
1135 M, OpenMPIRBuilder::Ident,
1136 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1138 M.getDataLayout().getDefaultGlobalsAddressSpace());
1139 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1140 GV->setAlignment(Align(8));
1141 Ident = GV;
1142 }
1143 }
1144
1145 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1146}
1147
1149 uint32_t &SrcLocStrSize) {
1150 SrcLocStrSize = LocStr.size();
1151 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1152 if (!SrcLocStr) {
1153 Constant *Initializer =
1154 ConstantDataArray::getString(M.getContext(), LocStr);
1155
1156 // Look for existing encoding of the location, not needed but minimizes the
1157 // difference to the existing solution while we transition.
1158 for (GlobalVariable &GV : M.globals())
1159 if (GV.isConstant() && GV.hasInitializer() &&
1160 GV.getInitializer() == Initializer)
1161 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1162
1163 SrcLocStr = Builder.CreateGlobalString(
1164 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1165 &M);
1166 }
1167 return SrcLocStr;
1168}
1169
1171 StringRef FileName,
1172 unsigned Line, unsigned Column,
1173 uint32_t &SrcLocStrSize) {
1174 SmallString<128> Buffer;
1175 Buffer.push_back(';');
1176 Buffer.append(FileName);
1177 Buffer.push_back(';');
1178 Buffer.append(FunctionName);
1179 Buffer.push_back(';');
1180 Buffer.append(std::to_string(Line));
1181 Buffer.push_back(';');
1182 Buffer.append(std::to_string(Column));
1183 Buffer.push_back(';');
1184 Buffer.push_back(';');
1185 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1186}
1187
1188Constant *
1190 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1191 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1192}
1193
1195 uint32_t &SrcLocStrSize,
1196 Function *F) {
1197 DILocation *DIL = DL.get();
1198 if (!DIL)
1199 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1200 StringRef FileName =
1201 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1202 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1203 if (Function.empty() && F)
1204 Function = F->getName();
1205 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1206 DIL->getColumn(), SrcLocStrSize);
1207}
1208
1210 uint32_t &SrcLocStrSize) {
1211 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1212 Loc.IP.getBlock()->getParent());
1213}
1214
1217 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1218 "omp_global_thread_num");
1219}
1220
1221OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1222 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1223 ArrayRef<Type *> ResultPtrTys,
1224 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1225 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1226 "expected one result pointer type per in_reduction item");
1227 if (!updateToLocation(Loc))
1228 return Loc.IP;
1229 if (OrigPtrs.empty())
1230 return Builder.saveIP();
1231
1232 // Compute the executing thread's gtid once for the whole target body and
1233 // reuse it for every in_reduction lookup, so a target with several
1234 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1235 // item.
1236 uint32_t SrcLocStrSize;
1237 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1238 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1239 Value *Gtid = getOrCreateThreadID(Ident);
1240
1241 // The runtime entry point takes (and returns) a generic, default-address-
1242 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1243 // taskgroups to find the matching task_reduction registration for the item.
1244 Type *PtrTy = PointerType::getUnqual(M.getContext());
1245 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1246 FunctionCallee GetThData =
1247 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1248
1249 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1250 // Normalize a non-default-address-space original pointer to the generic
1251 // address space before the call.
1252 Value *OrigPtr = OrigPtrs[Idx];
1253 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1254 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1255 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1256
1257 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1258 "omp.inred.priv");
1259
1260 // Cast the returned private pointer back to the requested address space
1261 // when it differs.
1262 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1263 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1264 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1265
1266 MapPrivateCB(Idx, Priv);
1267 }
1268 return Builder.saveIP();
1269}
1270
1273 bool ForceSimpleCall, bool CheckCancelFlag) {
1274 if (!updateToLocation(Loc))
1275 return Loc.IP;
1276
1277 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1278 // __kmpc_barrier(loc, thread_id);
1279
1280 IdentFlag BarrierLocFlags;
1281 switch (Kind) {
1282 case OMPD_for:
1283 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1284 break;
1285 case OMPD_sections:
1286 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1287 break;
1288 case OMPD_single:
1289 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1290 break;
1291 case OMPD_barrier:
1292 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1293 break;
1294 default:
1295 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1296 break;
1297 }
1298
1299 uint32_t SrcLocStrSize;
1300 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1301 Value *Args[] = {
1302 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1303 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1304
1305 // If we are in a cancellable parallel region, barriers are cancellation
1306 // points.
1307 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1308 bool UseCancelBarrier =
1309 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1310
1312 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1313 ? OMPRTL___kmpc_cancel_barrier
1314 : OMPRTL___kmpc_barrier),
1315 Args);
1316
1317 if (UseCancelBarrier && CheckCancelFlag)
1318 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1319 return Err;
1320
1321 return Builder.saveIP();
1322}
1323
1326 Value *IfCondition,
1327 omp::Directive CanceledDirective) {
1328 if (!updateToLocation(Loc))
1329 return Loc.IP;
1330
1331 // LLVM utilities like blocks with terminators.
1332 auto *UI = Builder.CreateUnreachable();
1333
1334 Instruction *ThenTI = UI, *ElseTI = nullptr;
1335 if (IfCondition) {
1336 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1337
1338 // Even if the if condition evaluates to false, this should count as a
1339 // cancellation point
1340 Builder.SetInsertPoint(ElseTI);
1341 auto ElseIP = Builder.saveIP();
1342
1344 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1345 if (!IPOrErr)
1346 return IPOrErr;
1347 }
1348
1349 Builder.SetInsertPoint(ThenTI);
1350
1351 Value *CancelKind = nullptr;
1352 switch (CanceledDirective) {
1353#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1354 case DirectiveEnum: \
1355 CancelKind = Builder.getInt32(Value); \
1356 break;
1357#include "llvm/Frontend/OpenMP/OMPKinds.def"
1358 default:
1359 llvm_unreachable("Unknown cancel kind!");
1360 }
1361
1362 uint32_t SrcLocStrSize;
1363 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1364 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1365 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1367 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1368
1369 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1370 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1371 return Err;
1372
1373 // Update the insertion point and remove the terminator we introduced.
1374 Builder.SetInsertPoint(UI->getParent());
1375 UI->eraseFromParent();
1376
1377 return Builder.saveIP();
1378}
1379
1382 omp::Directive CanceledDirective) {
1383 if (!updateToLocation(Loc))
1384 return Loc.IP;
1385
1386 // LLVM utilities like blocks with terminators.
1387 auto *UI = Builder.CreateUnreachable();
1388 Builder.SetInsertPoint(UI);
1389
1390 Value *CancelKind = nullptr;
1391 switch (CanceledDirective) {
1392#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1393 case DirectiveEnum: \
1394 CancelKind = Builder.getInt32(Value); \
1395 break;
1396#include "llvm/Frontend/OpenMP/OMPKinds.def"
1397 default:
1398 llvm_unreachable("Unknown cancel kind!");
1399 }
1400
1401 uint32_t SrcLocStrSize;
1402 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1403 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1404 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1406 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1407
1408 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1409 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1410 return Err;
1411
1412 // Update the insertion point and remove the terminator we introduced.
1413 Builder.SetInsertPoint(UI->getParent());
1414 UI->eraseFromParent();
1415
1416 return Builder.saveIP();
1417}
1418
1420 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1421 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1422 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1423 if (!updateToLocation(Loc))
1424 return Loc.IP;
1425
1426 Builder.restoreIP(AllocaIP);
1427 auto *KernelArgsPtr =
1428 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1430
1431 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1432 llvm::Value *Arg =
1433 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1434 Builder.CreateAlignedStore(
1435 KernelArgs[I], Arg,
1436 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1437 }
1438
1439 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1440 NumThreads, HostPtr, KernelArgsPtr};
1441
1443 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1444 OffloadingArgs);
1445
1446 return Builder.saveIP();
1447}
1448
1450 const LocationDescription &Loc, Value *OutlinedFnID,
1451 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1452 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1453
1454 if (!updateToLocation(Loc))
1455 return Loc.IP;
1456
1457 // On top of the arrays that were filled up, the target offloading call
1458 // takes as arguments the device id as well as the host pointer. The host
1459 // pointer is used by the runtime library to identify the current target
1460 // region, so it only has to be unique and not necessarily point to
1461 // anything. It could be the pointer to the outlined function that
1462 // implements the target region, but we aren't using that so that the
1463 // compiler doesn't need to keep that, and could therefore inline the host
1464 // function if proven worthwhile during optimization.
1465
1466 // From this point on, we need to have an ID of the target region defined.
1467 assert(OutlinedFnID && "Invalid outlined function ID!");
1468 (void)OutlinedFnID;
1469
1470 // Return value of the runtime offloading call.
1471 Value *Return = nullptr;
1472
1473 // Arguments for the target kernel.
1474 SmallVector<Value *> ArgsVector;
1475 getKernelArgsVector(Args, Builder, ArgsVector);
1476
1477 // The target region is an outlined function launched by the runtime
1478 // via calls to __tgt_target_kernel().
1479 //
1480 // Note that on the host and CPU targets, the runtime implementation of
1481 // these calls simply call the outlined function without forking threads.
1482 // The outlined functions themselves have runtime calls to
1483 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1484 // the compiler in emitTeamsCall() and emitParallelCall().
1485 //
1486 // In contrast, on the NVPTX target, the implementation of
1487 // __tgt_target_teams() launches a GPU kernel with the requested number
1488 // of teams and threads so no additional calls to the runtime are required.
1489 // Check the error code and execute the host version if required.
1490 Builder.restoreIP(emitTargetKernel(
1491 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1492 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1493
1494 BasicBlock *OffloadFailedBlock =
1495 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1496 BasicBlock *OffloadContBlock =
1497 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1498 Value *Failed = Builder.CreateIsNotNull(Return);
1499 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1500
1501 auto CurFn = Builder.GetInsertBlock()->getParent();
1502 emitBlock(OffloadFailedBlock, CurFn);
1503 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1504 if (!AfterIP)
1505 return AfterIP.takeError();
1506 Builder.restoreIP(*AfterIP);
1507 emitBranch(OffloadContBlock);
1508 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1509 return Builder.saveIP();
1510}
1511
1513 Value *CancelFlag, omp::Directive CanceledDirective) {
1514 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1515 "Unexpected cancellation!");
1516
1517 // For a cancel barrier we create two new blocks.
1518 BasicBlock *BB = Builder.GetInsertBlock();
1519 BasicBlock *NonCancellationBlock;
1520 if (Builder.GetInsertPoint() == BB->end()) {
1521 // TODO: This branch will not be needed once we moved to the
1522 // OpenMPIRBuilder codegen completely.
1523 NonCancellationBlock = BasicBlock::Create(
1524 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1525 } else {
1526 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1528 Builder.SetInsertPoint(BB);
1529 }
1530 BasicBlock *CancellationBlock = BasicBlock::Create(
1531 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1532
1533 // Jump to them based on the return value.
1534 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1535 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1536 /* TODO weight */ nullptr, nullptr);
1537
1538 // From the cancellation block we finalize all variables and go to the
1539 // post finalization block that is known to the FiniCB callback.
1540 auto &FI = FinalizationStack.back();
1541 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1542 if (!FiniBBOrErr)
1543 return FiniBBOrErr.takeError();
1544 Builder.SetInsertPoint(CancellationBlock);
1545 Builder.CreateBr(*FiniBBOrErr);
1546
1547 // The continuation block is where code generation continues.
1548 Builder.SetInsertPoint(NonCancellationBlock, NonCancellationBlock->begin());
1549 return Error::success();
1550}
1551
1552/// Create wrapper function used to gather the outlined function's argument
1553/// structure from a shared buffer and to forward them to it when running in
1554/// Generic mode.
1555///
1556/// The outlined function is expected to receive 2 integer arguments followed by
1557/// an optional pointer argument to an argument structure holding the rest.
1559 Function &OutlinedFn) {
1560 size_t NumArgs = OutlinedFn.arg_size();
1561 assert((NumArgs == 2 || NumArgs == 3) &&
1562 "expected a 2-3 argument parallel outlined function");
1563 bool UseArgStruct = NumArgs == 3;
1564
1565 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1566 IRBuilder<>::InsertPointGuard IPG(Builder);
1567 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1568 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1569 /*isVarArg=*/false);
1570 auto *WrapperFn =
1572 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1573
1574 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1575 WrapperFn->addParamAttr(0, Attribute::ZExt);
1576 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1577
1578 BasicBlock *EntryBB =
1579 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1580 Builder.SetInsertPoint(EntryBB);
1581
1582 // Allocation.
1583 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1584 /*ArraySize=*/nullptr, "addr");
1585 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1586 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1587 AddrAlloca->getName() + ".ascast");
1588
1589 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1590 /*ArraySize=*/nullptr, "zero");
1591 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1592 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1593 ZeroAlloca->getName() + ".ascast");
1594
1595 Value *ArgsAlloca = nullptr;
1596 if (UseArgStruct) {
1597 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1598 /*ArraySize=*/nullptr, "global_args");
1599 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1600 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1601 ArgsAlloca->getName() + ".ascast");
1602 }
1603
1604 // Initialization.
1605 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1606 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1607 if (UseArgStruct) {
1608 Builder.CreateCall(
1609 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1610 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1611 {ArgsAlloca});
1612 }
1613
1614 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1615
1616 // Load structArg from global_args.
1617 if (UseArgStruct) {
1618 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1619 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1620 {Builder.getInt64(0)});
1621 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1622 Args.push_back(StructArg);
1623 }
1624
1625 // Call the outlined function holding the parallel body.
1626 Builder.CreateCall(&OutlinedFn, Args);
1627 Builder.CreateRetVoid();
1628
1629 return WrapperFn;
1630}
1631
1632// Callback used to create OpenMP runtime calls to support
1633// omp parallel clause for the device.
1634// We need to use this callback to replace call to the OutlinedFn in OuterFn
1635// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1637 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1638 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1639 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1640 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1641 assert(OutlinedFn.arg_size() >= 2 &&
1642 "Expected at least tid and bounded tid as arguments");
1643 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1644
1645 // Add some known attributes.
1646 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1647 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1648 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1649 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1650 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1651 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1652
1653 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1654 assert(CI && "Expected call instruction to outlined function");
1655 CI->getParent()->setName("omp_parallel");
1656
1657 Builder.SetInsertPoint(CI);
1658 Type *PtrTy = OMPIRBuilder->VoidPtr;
1659
1660 // Add alloca for kernel args
1661 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1662 Builder.SetInsertPoint(OuterAllocaBB, OuterAllocaBB->getFirstInsertionPt());
1663 AllocaInst *ArgsAlloca =
1664 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1665 Value *Args = ArgsAlloca;
1666 // Add address space cast if array for storing arguments is not allocated
1667 // in address space 0
1668 if (ArgsAlloca->getAddressSpace())
1669 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1670 Builder.restoreIP(CurrentIP);
1671
1672 // Store captured vars which are used by kmpc_parallel_60
1673 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1674 Value *V = *(CI->arg_begin() + 2 + Idx);
1675 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1676 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1677 Builder.CreateStore(V, StoreAddress);
1678 }
1679
1680 Value *Cond =
1681 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1682 : Builder.getInt32(1);
1683 Value *NumThreadsArg =
1684 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1685 : Builder.getInt32(-1);
1686
1687 // If this is not a Generic kernel, we can skip generating the wrapper.
1688 Value *WrapperFn;
1689 if (isGenericKernel(*OuterFn))
1690 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1691 else
1692 WrapperFn = Constant::getNullValue(PtrTy);
1693
1694 // Build kmpc_parallel_60 call
1695 Value *Parallel60CallArgs[] = {
1696 /* identifier*/ Ident,
1697 /* global thread num*/ ThreadID,
1698 /* if expression */ Cond,
1699 /* number of threads */ NumThreadsArg,
1700 /* Proc bind */ Builder.getInt32(-1),
1701 /* outlined function */ &OutlinedFn,
1702 /* wrapper function */ WrapperFn,
1703 /* arguments of the outlined funciton*/ Args,
1704 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1705 /* strict for number of threads */ Builder.getInt32(0)};
1706
1707 FunctionCallee RTLFn =
1708 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1709
1710 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1711
1712 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1713 << *Builder.GetInsertBlock()->getParent() << "\n");
1714
1715 // Initialize the local TID stack location with the argument value.
1716 Builder.SetInsertPoint(PrivTID);
1717 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1718 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1719 PrivTIDAddr);
1720
1721 // Remove redundant call to the outlined function.
1722 CI->eraseFromParent();
1723
1724 for (Instruction *I : ToBeDeleted) {
1725 I->eraseFromParent();
1726 }
1727}
1728
1729// Callback used to create OpenMP runtime calls to support
1730// omp parallel clause for the host.
1731// We need to use this callback to replace call to the OutlinedFn in OuterFn
1732// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1733static void
1735 Function *OuterFn, Value *Ident, Value *IfCondition,
1736 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1737 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1738 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1739 FunctionCallee RTLFn;
1740 if (IfCondition) {
1741 RTLFn =
1742 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1743 } else {
1744 RTLFn =
1745 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1746 }
1747 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1748 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1749 LLVMContext &Ctx = F->getContext();
1750 MDBuilder MDB(Ctx);
1751 // Annotate the callback behavior of the __kmpc_fork_call:
1752 // - The callback callee is argument number 2 (microtask).
1753 // - The first two arguments of the callback callee are unknown (-1).
1754 // - All variadic arguments to the __kmpc_fork_call are passed to the
1755 // callback callee.
1756 F->addMetadata(LLVMContext::MD_callback,
1758 2, {-1, -1},
1759 /* VarArgsArePassed */ true)}));
1760 }
1761 }
1762 // Add some known attributes.
1763 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1764 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1765 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1766
1767 assert(OutlinedFn.arg_size() >= 2 &&
1768 "Expected at least tid and bounded tid as arguments");
1769 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1770
1771 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1772 CI->getParent()->setName("omp_parallel");
1773 Builder.SetInsertPoint(CI);
1774
1775 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1776 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1777 &OutlinedFn};
1778
1779 SmallVector<Value *, 16> RealArgs;
1780 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1781 if (IfCondition) {
1782 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1783 RealArgs.push_back(Cond);
1784 }
1785 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1786
1787 // __kmpc_fork_call_if always expects a void ptr as the last argument
1788 // If there are no arguments, pass a null pointer.
1789 auto PtrTy = OMPIRBuilder->VoidPtr;
1790 if (IfCondition && NumCapturedVars == 0) {
1791 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1792 RealArgs.push_back(NullPtrValue);
1793 }
1794
1795 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1796
1797 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1798 << *Builder.GetInsertBlock()->getParent() << "\n");
1799
1800 // Initialize the local TID stack location with the argument value.
1801 Builder.SetInsertPoint(PrivTID);
1802 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1803 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1804 PrivTIDAddr);
1805
1806 // Remove redundant call to the outlined function.
1807 CI->eraseFromParent();
1808
1809 for (Instruction *I : ToBeDeleted) {
1810 I->eraseFromParent();
1811 }
1812}
1813
1815 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1816 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1817 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1818 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1819 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1820
1821 if (!updateToLocation(Loc))
1822 return Loc.IP;
1823
1824 uint32_t SrcLocStrSize;
1825 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1826 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1827 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1828 (ProcBind != OMP_PROC_BIND_default);
1829 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1830 // If we generate code for the target device, we need to allocate
1831 // struct for aggregate params in the device default alloca address space.
1832 // OpenMP runtime requires that the params of the extracted functions are
1833 // passed as zero address space pointers. This flag ensures that extracted
1834 // function arguments are declared in zero address space
1835 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1836
1837 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1838 // only if we compile for host side.
1839 if (NumThreads && !Config.isTargetDevice()) {
1840 Value *Args[] = {
1841 Ident, ThreadID,
1842 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
1844 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
1845 }
1846
1847 if (ProcBind != OMP_PROC_BIND_default) {
1848 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
1849 Value *Args[] = {
1850 Ident, ThreadID,
1851 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
1853 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
1854 }
1855
1856 BasicBlock *InsertBB = Builder.GetInsertBlock();
1857 Function *OuterFn = InsertBB->getParent();
1858
1859 // Save the outer alloca block because the insertion iterator may get
1860 // invalidated and we still need this later.
1861 BasicBlock *OuterAllocaBlock = OuterAllocIP.getBlock();
1862
1863 // Vector to remember instructions we used only during the modeling but which
1864 // we want to delete at the end.
1866
1867 // Change the location to the outer alloca insertion point to create and
1868 // initialize the allocas we pass into the parallel region.
1869 InsertPointTy NewOuter(OuterAllocaBlock, OuterAllocaBlock->begin());
1870 Builder.restoreIP(NewOuter);
1871 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
1872 AllocaInst *ZeroAddrAlloca =
1873 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
1874 Instruction *TIDAddr = TIDAddrAlloca;
1875 Instruction *ZeroAddr = ZeroAddrAlloca;
1876 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
1877 // Add additional casts to enforce pointers in zero address space
1878 TIDAddr = new AddrSpaceCastInst(
1879 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
1880 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
1881 ToBeDeleted.push_back(TIDAddr);
1882 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
1883 PointerType ::get(M.getContext(), 0),
1884 "zero.addr.ascast");
1885 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
1886 ToBeDeleted.push_back(ZeroAddr);
1887 }
1888
1889 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
1890 // associated arguments in the outlined function, so we delete them later.
1891 ToBeDeleted.push_back(TIDAddrAlloca);
1892 ToBeDeleted.push_back(ZeroAddrAlloca);
1893
1894 // Create an artificial insertion point that will also ensure the blocks we
1895 // are about to split are not degenerated.
1896 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
1897
1898 BasicBlock *EntryBB = UI->getParent();
1899 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
1900 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
1901 BasicBlock *PRegPreFiniBB =
1902 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
1903 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
1904
1905 auto FiniCBWrapper = [&](InsertPointTy IP) {
1906 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
1907 // target to the region exit block.
1908 if (IP.getBlock()->end() == IP.getPoint()) {
1910 Builder.restoreIP(IP);
1911 Instruction *I = Builder.CreateBr(PRegExitBB);
1912 IP = InsertPointTy(I->getParent(), I->getIterator());
1913 }
1914 assert(IP.getBlock()->getTerminator()->getNumSuccessors() == 1 &&
1915 IP.getBlock()->getTerminator()->getSuccessor(0) == PRegExitBB &&
1916 "Unexpected insertion point for finalization call!");
1917 return FiniCB(IP);
1918 };
1919
1920 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
1921
1922 // Generate the privatization allocas in the block that will become the entry
1923 // of the outlined function.
1924 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
1925 InsertPointTy InnerAllocaIP = Builder.saveIP();
1926
1927 AllocaInst *PrivTIDAddr =
1928 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
1929 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
1930
1931 // Add some fake uses for OpenMP provided arguments.
1932 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
1933 Instruction *ZeroAddrUse =
1934 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
1935 ToBeDeleted.push_back(ZeroAddrUse);
1936
1937 // EntryBB
1938 // |
1939 // V
1940 // PRegionEntryBB <- Privatization allocas are placed here.
1941 // |
1942 // V
1943 // PRegionBodyBB <- BodeGen is invoked here.
1944 // |
1945 // V
1946 // PRegPreFiniBB <- The block we will start finalization from.
1947 // |
1948 // V
1949 // PRegionExitBB <- A common exit to simplify block collection.
1950 //
1951
1952 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
1953
1954 // Let the caller create the body.
1955 assert(BodyGenCB && "Expected body generation callback!");
1956 InsertPointTy CodeGenIP(PRegBodyBB, PRegBodyBB->begin());
1957 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
1958 return Err;
1959
1960 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
1961
1962 // If OuterFn is a Generic kernel, we need to use device shared memory to
1963 // allocate argument structures. Otherwise, we use stack allocations as usual.
1964 bool UsesDeviceSharedMemory =
1965 Config.isTargetDevice() && isGenericKernel(*OuterFn);
1966 std::unique_ptr<OutlineInfo> OI =
1967 UsesDeviceSharedMemory
1968 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
1969 : std::make_unique<OutlineInfo>();
1970
1971 if (Config.isTargetDevice()) {
1972 // Generate OpenMP target specific runtime call
1973 OI->PostOutlineCB = [=, ToBeDeletedVec =
1974 std::move(ToBeDeleted)](Function &OutlinedFn) {
1975 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
1976 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
1977 ThreadID, ToBeDeletedVec);
1978 };
1979 } else {
1980 // Generate OpenMP host runtime call
1981 OI->PostOutlineCB = [=, ToBeDeletedVec =
1982 std::move(ToBeDeleted)](Function &OutlinedFn) {
1983 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
1984 PrivTID, PrivTIDAddr, ToBeDeletedVec);
1985 };
1986 }
1987
1988 OI->FixUpNonEntryAllocas = true;
1989 OI->OuterAllocBB = OuterAllocaBlock;
1990 OI->EntryBB = PRegEntryBB;
1991 OI->ExitBB = PRegExitBB;
1992 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
1993 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
1994
1995 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
1997 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
1998
1999 CodeExtractorAnalysisCache CEAC(*OuterFn);
2000 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2001 /* AggregateArgs */ false,
2002 /* BlockFrequencyInfo */ nullptr,
2003 /* BranchProbabilityInfo */ nullptr,
2004 /* AssumptionCache */ nullptr,
2005 /* AllowVarArgs */ true,
2006 /* AllowAlloca */ true,
2007 /* AllocationBlock */ OuterAllocaBlock,
2008 /* DeallocationBlocks */ {},
2009 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2010
2011 // Find inputs to, outputs from the code region.
2012 BasicBlock *CommonExit = nullptr;
2013 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2014 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2015
2016 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2017 /*CollectGlobalInputs=*/true);
2018
2019 Inputs.remove_if([&](Value *I) {
2021 return GV->getValueType() == OpenMPIRBuilder::Ident;
2022
2023 return false;
2024 });
2025
2026 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2027
2028 FunctionCallee TIDRTLFn =
2029 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2030
2031 auto PrivHelper = [&](Value &V) -> Error {
2032 if (&V == TIDAddr || &V == ZeroAddr) {
2033 OI->ExcludeArgsFromAggregate.push_back(&V);
2034 return Error::success();
2035 }
2036
2038 for (Use &U : V.uses())
2039 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2040 if (ParallelRegionBlockSet.count(UserI->getParent()))
2041 Uses.insert(&U);
2042
2043 // __kmpc_fork_call expects extra arguments as pointers. If the input
2044 // already has a pointer type, everything is fine. Otherwise, store the
2045 // value onto stack and load it back inside the to-be-outlined region. This
2046 // will ensure only the pointer will be passed to the function.
2047 // FIXME: if there are more than 15 trailing arguments, they must be
2048 // additionally packed in a struct.
2049 Value *Inner = &V;
2050 if (!V.getType()->isPointerTy()) {
2052 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2053
2054 Builder.restoreIP(OuterAllocIP);
2055 Value *Ptr;
2056 if (UsesDeviceSharedMemory) {
2057 // Use device shared memory instead, if needed.
2058 Ptr = createOMPAllocShared(OuterAllocIP, V.getType(),
2059 V.getName() + ".reloaded");
2060 for (BasicBlock *DeallocBlock : OuterDeallocBlocks)
2062 InsertPointTy(DeallocBlock, DeallocBlock->getFirstInsertionPt()),
2063 Ptr, V.getType());
2064 } else {
2065 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2066 V.getName() + ".reloaded");
2067 }
2068
2069 // Store to stack at end of the block that currently branches to the entry
2070 // block of the to-be-outlined region.
2071 Builder.SetInsertPoint(InsertBB,
2072 InsertBB->getTerminator()->getIterator());
2073 Builder.CreateStore(&V, Ptr);
2074
2075 // Load back next to allocations in the to-be-outlined region.
2076 Builder.restoreIP(InnerAllocaIP);
2077 Inner = Builder.CreateLoad(V.getType(), Ptr);
2078 }
2079
2080 Value *ReplacementValue = nullptr;
2081 CallInst *CI = dyn_cast<CallInst>(&V);
2082 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2083 ReplacementValue = PrivTID;
2084 } else {
2085 InsertPointOrErrorTy AfterIP =
2086 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2087 if (!AfterIP)
2088 return AfterIP.takeError();
2089 Builder.restoreIP(*AfterIP);
2090 InnerAllocaIP = {
2091 InnerAllocaIP.getBlock(),
2092 InnerAllocaIP.getBlock()->getTerminator()->getIterator()};
2093
2094 assert(ReplacementValue &&
2095 "Expected copy/create callback to set replacement value!");
2096 if (ReplacementValue == &V)
2097 return Error::success();
2098 }
2099
2100 for (Use *UPtr : Uses)
2101 UPtr->set(ReplacementValue);
2102
2103 return Error::success();
2104 };
2105
2106 // Reset the inner alloca insertion as it will be used for loading the values
2107 // wrapped into pointers before passing them into the to-be-outlined region.
2108 // Configure it to insert immediately after the fake use of zero address so
2109 // that they are available in the generated body and so that the
2110 // OpenMP-related values (thread ID and zero address pointers) remain leading
2111 // in the argument list.
2112 InnerAllocaIP = IRBuilder<>::InsertPoint(
2113 ZeroAddrUse->getParent(), ZeroAddrUse->getNextNode()->getIterator());
2114
2115 // Reset the outer alloca insertion point to the entry of the relevant block
2116 // in case it was invalidated.
2117 OuterAllocIP = IRBuilder<>::InsertPoint(
2118 OuterAllocaBlock, OuterAllocaBlock->getFirstInsertionPt());
2119
2120 for (Value *Input : Inputs) {
2121 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2122 if (Error Err = PrivHelper(*Input))
2123 return Err;
2124 }
2125 LLVM_DEBUG({
2126 for (Value *Output : Outputs)
2127 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2128 });
2129 assert(Outputs.empty() &&
2130 "OpenMP outlining should not produce live-out values!");
2131
2132 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2133 LLVM_DEBUG({
2134 for (auto *BB : Blocks)
2135 dbgs() << " PBR: " << BB->getName() << "\n";
2136 });
2137
2138 // Adjust the finalization stack, verify the adjustment, and call the
2139 // finalize function a last time to finalize values between the pre-fini
2140 // block and the exit block if we left the parallel "the normal way".
2141 auto FiniInfo = FinalizationStack.pop_back_val();
2142 (void)FiniInfo;
2143 assert(FiniInfo.DK == OMPD_parallel &&
2144 "Unexpected finalization stack state!");
2145
2146 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2147
2148 InsertPointTy PreFiniIP(PRegPreFiniBB, PRegPreFiniTI->getIterator());
2149 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2150 if (!FiniBBOrErr)
2151 return FiniBBOrErr.takeError();
2152 {
2154 Builder.restoreIP(PreFiniIP);
2155 Builder.CreateBr(*FiniBBOrErr);
2156 // There's currently a branch to omp.par.exit. Delete it. We will get there
2157 // via the fini block
2158 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2159 Term->eraseFromParent();
2160 }
2161
2162 // Register the outlined info.
2163 addOutlineInfo(std::move(OI));
2164
2165 InsertPointTy AfterIP(UI->getParent(), UI->getParent()->end());
2166 UI->eraseFromParent();
2167
2168 return AfterIP;
2169}
2170
2172 // Build call void __kmpc_flush(ident_t *loc)
2173 uint32_t SrcLocStrSize;
2174 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2175 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2176
2178 Args);
2179}
2180
2182 if (!updateToLocation(Loc))
2183 return;
2184 emitFlush(Loc);
2185}
2186
2188 Value *Message) {
2189 if (!updateToLocation(Loc))
2190 return;
2191
2192 // Build call void __kmpc_error(ident_t *loc, int severity,
2193 // const char *message)
2194 uint32_t SrcLocStrSize;
2195 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2196 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2197 // Severity: 1 = warning, 2 = fatal.
2198 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2199 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2200 Value *Args[] = {Ident, Severity, MessageArg};
2201
2203 Args);
2204}
2205
2207 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2208 uint32_t SrcLocStrSize;
2209 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2210 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2211 Constant *I32Null = ConstantInt::getNullValue(Int32);
2212 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2213
2215 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2216}
2217
2223
2225 const DependData &Dep) {
2226 // Store the pointer to the variable
2227 Value *Addr = Builder.CreateStructGEP(
2228 DependInfo, Entry,
2229 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2230 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2231 Builder.CreateStore(DepValPtr, Addr);
2232 // Store the size of the variable
2233 Value *Size = Builder.CreateStructGEP(
2234 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2235 Builder.CreateStore(
2236 ConstantInt::get(SizeTy,
2237 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2238 Size);
2239 // Store the dependency kind
2240 Value *Flags = Builder.CreateStructGEP(
2241 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2242 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2243 static_cast<unsigned int>(Dep.DepKind)),
2244 Flags);
2245}
2246
2247// Processes the dependencies in Dependencies and does the following
2248// - Allocates space on the stack of an array of DependInfo objects
2249// - Populates each DependInfo object with relevant information of
2250// the corresponding dependence.
2251// - All code is inserted in the entry block of the current function.
2253 OpenMPIRBuilder &OMPBuilder,
2255 // Early return if we have no dependencies to process
2256 if (Dependencies.empty())
2257 return nullptr;
2258
2259 // Given a vector of DependData objects, in this function we create an
2260 // array on the stack that holds kmp_depend_info objects corresponding
2261 // to each dependency. This is then passed to the OpenMP runtime.
2262 // For example, if there are 'n' dependencies then the following psedo
2263 // code is generated. Assume the first dependence is on a variable 'a'
2264 //
2265 // \code{c}
2266 // DepArray = alloc(n x sizeof(kmp_depend_info);
2267 // idx = 0;
2268 // DepArray[idx].base_addr = ptrtoint(&a);
2269 // DepArray[idx].len = 8;
2270 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2271 // ++idx;
2272 // DepArray[idx].base_addr = ...;
2273 // \endcode
2274
2275 IRBuilderBase &Builder = OMPBuilder.Builder;
2276 Type *DependInfo = OMPBuilder.DependInfo;
2277
2278 Value *DepArray = nullptr;
2279 OpenMPIRBuilder::InsertPointTy OldIP = Builder.saveIP();
2280 Builder.SetInsertPoint(
2282
2283 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2284 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2285
2286 Builder.restoreIP(OldIP);
2287
2288 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2289 Value *Base =
2290 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2291 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2292 }
2293 return DepArray;
2294}
2295
2297 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2298 // global_tid);
2299 uint32_t SrcLocStrSize;
2300 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2301 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2302 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2303
2304 // Ignore return result until untied tasks are supported.
2306 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2307}
2308
2310 DependenciesInfo Dependencies) {
2311 if (!updateToLocation(Loc))
2312 return;
2313
2314 Value *DepArray = nullptr;
2315 Type *DepArrayTy = nullptr;
2316 Value *NumDeps = nullptr;
2317 if (Dependencies.DepArray) {
2318 DepArray = Dependencies.DepArray;
2319 NumDeps = Dependencies.NumDeps;
2320 } else if (!Dependencies.Deps.empty()) {
2321 InsertPointTy OldIP = Builder.saveIP();
2322 BasicBlock &entryBB =
2323 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2324 Builder.SetInsertPoint(&entryBB, entryBB.getFirstInsertionPt());
2325
2326 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2327 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2328 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2329
2330 Builder.restoreIP(OldIP);
2331 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2332 Value *Base =
2333 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2334 this->emitTaskDependency(Builder, Base, Dep);
2335 }
2336 }
2337
2338 if (DepArray) {
2339 uint32_t SrcLocStrSize;
2340 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2341 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2342 Value *Args[] = {
2343 Ident,
2344 getOrCreateThreadID(Ident),
2345 NumDeps,
2346 DepArray,
2347 ConstantInt::get(Builder.getInt32Ty(), 0),
2349 ConstantInt::get(Builder.getInt32Ty(), false)};
2352 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2353 Args);
2354 } else {
2356 }
2357}
2358
2359/// Create the task duplication function passed to kmpc_taskloop.
2360Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2361 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2362 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2363 if (!DupCB)
2365 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2366
2367 // From OpenMP Runtime p_task_dup_t:
2368 // Routine optionally generated by the compiler for setting the lastprivate
2369 // flag and calling needed constructors for private/firstprivate objects (used
2370 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2371 // lastprivate flag.
2372 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2373
2374 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2375
2376 FunctionType *DupFuncTy = FunctionType::get(
2377 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2378 /*isVarArg=*/false);
2379
2380 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2381 "omp_taskloop_dup", M);
2382 Value *DestTaskArg = DupFunction->getArg(0);
2383 Value *SrcTaskArg = DupFunction->getArg(1);
2384 Value *LastprivateFlagArg = DupFunction->getArg(2);
2385 DestTaskArg->setName("dest_task");
2386 SrcTaskArg->setName("src_task");
2387 LastprivateFlagArg->setName("lastprivate_flag");
2388
2389 IRBuilderBase::InsertPointGuard Guard(Builder);
2390 Builder.SetInsertPoint(
2391 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2392
2393 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2394 Type *TaskWithPrivatesTy =
2395 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2396 Value *TaskPrivates = Builder.CreateGEP(
2397 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2398 Value *ContextPtr = Builder.CreateGEP(
2399 PrivatesTy, TaskPrivates,
2400 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2401 return ContextPtr;
2402 };
2403
2404 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2405 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2406
2407 DestTaskContextPtr->setName("destPtr");
2408 SrcTaskContextPtr->setName("srcPtr");
2409
2410 InsertPointTy AllocaIP(&DupFunction->getEntryBlock(),
2411 DupFunction->getEntryBlock().begin());
2412 InsertPointTy CodeGenIP = Builder.saveIP();
2413 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2414 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2415 if (!AfterIPOrError)
2416 return AfterIPOrError.takeError();
2417 Builder.restoreIP(*AfterIPOrError);
2418
2419 Builder.CreateRetVoid();
2420
2421 return DupFunction;
2422}
2423
2424OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2425 const LocationDescription &Loc, InsertPointTy AllocaIP,
2426 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2427 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2428 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2429 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2430 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2431 Value *TaskContextStructPtrVal) {
2432
2433 if (!updateToLocation(Loc))
2434 return InsertPointTy();
2435
2436 uint32_t SrcLocStrSize;
2437 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2438 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2439
2440 BasicBlock *TaskloopExitBB =
2441 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2442 BasicBlock *TaskloopBodyBB =
2443 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2444 BasicBlock *TaskloopAllocaBB =
2445 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2446
2447 InsertPointTy TaskloopAllocaIP =
2448 InsertPointTy(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2449 InsertPointTy TaskloopBodyIP =
2450 InsertPointTy(TaskloopBodyBB, TaskloopBodyBB->begin());
2451
2452 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2453 return Err;
2454
2455 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2456 if (!result) {
2457 return result.takeError();
2458 }
2459
2460 llvm::CanonicalLoopInfo *CLI = result.get();
2461 auto OI = std::make_unique<OutlineInfo>();
2462 OI->EntryBB = TaskloopAllocaBB;
2463 OI->OuterAllocBB = AllocaIP.getBlock();
2464 OI->ExitBB = TaskloopExitBB;
2465 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2466 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2467
2468 // Add the thread ID argument.
2469 SmallVector<Instruction *> ToBeDeleted;
2470 // dummy instruction to be used as a fake argument
2471 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2472 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2473 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2474 TaskloopAllocaIP, "lb", false, true);
2475 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2476 TaskloopAllocaIP, "ub", false, true);
2477 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2478 TaskloopAllocaIP, "step", false, true);
2479 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2480 // aggregate struct
2481 OI->Inputs.insert(FakeLB);
2482 OI->Inputs.insert(FakeUB);
2483 OI->Inputs.insert(FakeStep);
2484 if (TaskContextStructPtrVal)
2485 OI->Inputs.insert(TaskContextStructPtrVal);
2486 assert(((TaskContextStructPtrVal && DupCB) ||
2487 (!TaskContextStructPtrVal && !DupCB)) &&
2488 "Task context struct ptr and duplication callback must be both set "
2489 "or both null");
2490
2491 // It isn't safe to run the duplication bodygen callback inside the post
2492 // outlining callback so this has to be run now before we know the real task
2493 // shareds structure type.
2494 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2495 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2496 Type *FakeSharedsTy = StructType::get(
2497 Builder.getContext(),
2498 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2499 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2500 FakeSharedsTy,
2501 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2502 if (!TaskDupFnOrErr) {
2503 return TaskDupFnOrErr.takeError();
2504 }
2505 Value *TaskDupFn = *TaskDupFnOrErr;
2506
2507 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2508 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2509 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2510 FakeSharedsTy, Final, Mergeable, Priority,
2511 NumOfCollapseLoops](Function &OutlinedFn) mutable {
2512 // Replace the Stale CI by appropriate RTL function call.
2513 assert(OutlinedFn.hasOneUse() &&
2514 "there must be a single user for the outlined function");
2515 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2516
2517 /* Create the casting for the Bounds Values that can be used when outlining
2518 * to replace the uses of the fakes with real values */
2519 BasicBlock *CodeReplBB = StaleCI->getParent();
2520 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2521 Value *CastedLBVal =
2522 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2523 Value *CastedUBVal =
2524 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2525 Value *CastedStepVal =
2526 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2527
2528 Builder.SetInsertPoint(StaleCI);
2529
2530 // Gather the arguments for emitting the runtime call for
2531 // @__kmpc_omp_task_alloc
2532 Function *TaskAllocFn =
2533 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2534
2535 Value *ThreadID = getOrCreateThreadID(Ident);
2536
2537 if (!NoGroup) {
2538 // Emit runtime call for @__kmpc_taskgroup
2539 Function *TaskgroupFn =
2540 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2541 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2542 }
2543
2544 // `flags` Argument Configuration
2545 // Task is tied if (Flags & 1) == 1.
2546 // Task is untied if (Flags & 1) == 0.
2547 // Task is final if (Flags & 2) == 2.
2548 // Task is not final if (Flags & 2) == 0.
2549 // Task is mergeable if (Flags & 4) == 4.
2550 // Task is not mergeable if (Flags & 4) == 0.
2551 // Task is priority if (Flags & 32) == 32.
2552 // Task is not priority if (Flags & 32) == 0.
2553 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2554 if (Final)
2555 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2556 if (Mergeable)
2557 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2558 if (Priority)
2559 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2560
2561 Value *TaskSize = Builder.getInt64(
2562 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2563
2564 AllocaInst *ArgStructAlloca =
2566 assert(ArgStructAlloca &&
2567 "Unable to find the alloca instruction corresponding to arguments "
2568 "for extracted function");
2569 std::optional<TypeSize> ArgAllocSize =
2570 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2571 assert(ArgAllocSize &&
2572 "Unable to determine size of arguments for extracted function");
2573 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2574
2575 // Emit the @__kmpc_omp_task_alloc runtime call
2576 // The runtime call returns a pointer to an area where the task captured
2577 // variables must be copied before the task is run (TaskData)
2578 CallInst *TaskData = Builder.CreateCall(
2579 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2580 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2581 /*task_func=*/&OutlinedFn});
2582
2583 Value *Shareds = StaleCI->getArgOperand(1);
2584 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2585 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2586 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2587 SharedsSize);
2588 // Get the pointer to loop lb, ub, step from task ptr
2589 // and set up the lowerbound,upperbound and step values
2590 llvm::Value *Lb = Builder.CreateGEP(
2591 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2592
2593 llvm::Value *Ub = Builder.CreateGEP(
2594 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2595
2596 llvm::Value *Step = Builder.CreateGEP(
2597 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2598 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2599
2600 // set up the arguments for emitting kmpc_taskloop runtime call
2601 // setting values for ifval, nogroup, sched, grainsize, task_dup
2602 Value *IfCondVal =
2603 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2604 : Builder.getInt32(1);
2605 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2606 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2607 Value *NoGroupVal = Builder.getInt32(1);
2608 Value *SchedVal = Builder.getInt32(Sched);
2609 Value *GrainSizeVal =
2610 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2611 : Builder.getInt64(0);
2612 Value *TaskDup = TaskDupFn;
2613
2614 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2615 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2616
2617 // taskloop runtime call
2618 Function *TaskloopFn =
2619 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2620 Builder.CreateCall(TaskloopFn, Args);
2621
2622 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2623 // nogroup is not defined
2624 if (!NoGroup) {
2625 Function *EndTaskgroupFn =
2626 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2627 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2628 }
2629
2630 StaleCI->eraseFromParent();
2631
2632 Builder.SetInsertPoint(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2633
2634 LoadInst *SharedsOutlined =
2635 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2636 OutlinedFn.getArg(1)->replaceUsesWithIf(
2637 SharedsOutlined,
2638 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2639
2640 Value *IV = CLI->getIndVar();
2641 Type *IVTy = IV->getType();
2642 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2643
2644 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2645 // UpperBound. These GEP's can be reused for loading the tasks respective
2646 // bounds.
2647 Value *TaskLB = nullptr;
2648 Value *TaskUB = nullptr;
2649 Value *TaskStep = nullptr;
2650 Value *LoadTaskLB = nullptr;
2651 Value *LoadTaskUB = nullptr;
2652 Value *LoadTaskStep = nullptr;
2653 for (Instruction &I : *TaskloopAllocaBB) {
2654 if (I.getOpcode() == Instruction::GetElementPtr) {
2655 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2656 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2657 switch (CI->getZExtValue()) {
2658 case 0:
2659 TaskLB = &I;
2660 break;
2661 case 1:
2662 TaskUB = &I;
2663 break;
2664 case 2:
2665 TaskStep = &I;
2666 break;
2667 }
2668 }
2669 } else if (I.getOpcode() == Instruction::Load) {
2670 LoadInst &Load = cast<LoadInst>(I);
2671 if (Load.getPointerOperand() == TaskLB) {
2672 assert(TaskLB != nullptr && "Expected value for TaskLB");
2673 LoadTaskLB = &I;
2674 } else if (Load.getPointerOperand() == TaskUB) {
2675 assert(TaskUB != nullptr && "Expected value for TaskUB");
2676 LoadTaskUB = &I;
2677 } else if (Load.getPointerOperand() == TaskStep) {
2678 assert(TaskStep != nullptr && "Expected value for TaskStep");
2679 LoadTaskStep = &I;
2680 }
2681 }
2682 }
2683
2684 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2685
2686 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2687 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2688 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2689 Value *TripCountMinusOne = Builder.CreateSDiv(
2690 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2691 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2692 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2693 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2694 // set the trip count in the CLI
2695 CLI->setTripCount(CastedTripCount);
2696
2697 Builder.SetInsertPoint(CLI->getBody(),
2698 CLI->getBody()->getFirstInsertionPt());
2699
2700 if (NumOfCollapseLoops > 1) {
2701 llvm::SmallVector<User *> UsersToReplace;
2702 // When using the collapse clause, the bounds of the loop have to be
2703 // adjusted to properly represent the iterator of the outer loop.
2704 Value *IVPlusTaskLB = Builder.CreateAdd(
2705 CLI->getIndVar(),
2706 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2707 // To ensure every Use is correctly captured, we first want to record
2708 // which users to replace the value in, and then replace the value.
2709 for (auto IVUse = CLI->getIndVar()->uses().begin();
2710 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2711 User *IVUser = IVUse->getUser();
2712 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2713 if (Op->getOpcode() == Instruction::URem ||
2714 Op->getOpcode() == Instruction::UDiv) {
2715 UsersToReplace.push_back(IVUser);
2716 }
2717 }
2718 }
2719 for (User *User : UsersToReplace) {
2720 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2721 }
2722 } else {
2723 // The canonical loop is generated with a fixed lower bound. We need to
2724 // update the index calculation code to use the task's lower bound. The
2725 // generated code looks like this:
2726 // %omp_loop.iv = phi ...
2727 // ...
2728 // %tmp = mul [type] %omp_loop.iv, step
2729 // %user_index = add [type] tmp, lb
2730 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2731 // of the normalised induction variable:
2732 // 1. This one: converting the normalised IV to the user IV
2733 // 2. The increment (add)
2734 // 3. The comparison against the trip count (icmp)
2735 // (1) is the only use that is a mul followed by an add so this cannot
2736 // match other IR.
2737 assert(CLI->getIndVar()->getNumUses() == 3 &&
2738 "Canonical loop should have exactly three uses of the ind var");
2739 for (User *IVUser : CLI->getIndVar()->users()) {
2740 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2741 if (Mul->getOpcode() == Instruction::Mul) {
2742 for (User *MulUser : Mul->users()) {
2743 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2744 if (Add->getOpcode() == Instruction::Add) {
2745 Add->setOperand(1, CastedTaskLB);
2746 }
2747 }
2748 }
2749 }
2750 }
2751 }
2752 }
2753
2754 FakeLB->replaceAllUsesWith(CastedLBVal);
2755 FakeUB->replaceAllUsesWith(CastedUBVal);
2756 FakeStep->replaceAllUsesWith(CastedStepVal);
2757 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2758 I->eraseFromParent();
2759 }
2760 };
2761
2762 addOutlineInfo(std::move(OI));
2763 Builder.SetInsertPoint(TaskloopExitBB, TaskloopExitBB->begin());
2764 return Builder.saveIP();
2765}
2766
2769 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2771 llvm::Type::getInt32Ty(M.getContext()));
2772}
2773
2775 const LocationDescription &Loc, InsertPointTy AllocaIP,
2776 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2777 bool Tied, Value *Final, Value *IfCondition,
2778 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2779 bool Mergeable, Value *EventHandle, Value *Priority) {
2780
2781 if (!updateToLocation(Loc))
2782 return InsertPointTy();
2783
2784 uint32_t SrcLocStrSize;
2785 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2786 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2787 // The current basic block is split into four basic blocks. After outlining,
2788 // they will be mapped as follows:
2789 // ```
2790 // def current_fn() {
2791 // current_basic_block:
2792 // br label %task.exit
2793 // task.exit:
2794 // ; instructions after task
2795 // }
2796 // def outlined_fn() {
2797 // task.alloca:
2798 // br label %task.body
2799 // task.body:
2800 // ret void
2801 // }
2802 // ```
2803 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2804 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2805 BasicBlock *TaskAllocaBB =
2806 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2807
2808 InsertPointTy TaskAllocaIP =
2809 InsertPointTy(TaskAllocaBB, TaskAllocaBB->begin());
2810 InsertPointTy TaskBodyIP = InsertPointTy(TaskBodyBB, TaskBodyBB->begin());
2811 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2812 return Err;
2813
2814 auto OI = std::make_unique<OutlineInfo>();
2815 OI->EntryBB = TaskAllocaBB;
2816 OI->OuterAllocBB = AllocaIP.getBlock();
2817 OI->ExitBB = TaskExitBB;
2818 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2819 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2820
2821 // Add the thread ID argument.
2823 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2824 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2825
2826 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2827 Affinities, Mergeable, Priority, EventHandle,
2828 TaskAllocaBB,
2829 ToBeDeleted](Function &OutlinedFn) mutable {
2830 // Replace the Stale CI by appropriate RTL function call.
2831 assert(OutlinedFn.hasOneUse() &&
2832 "there must be a single user for the outlined function");
2833 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2834
2835 // HasShareds is true if any variables are captured in the outlined region,
2836 // false otherwise.
2837 bool HasShareds = StaleCI->arg_size() > 1;
2838 Builder.SetInsertPoint(StaleCI);
2839
2840 // Gather the arguments for emitting the runtime call for
2841 // @__kmpc_omp_task_alloc
2842 Function *TaskAllocFn =
2843 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2844
2845 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
2846 // call.
2847 Value *ThreadID = getOrCreateThreadID(Ident);
2848
2849 // Argument - `flags`
2850 // Task is tied iff (Flags & 1) == 1.
2851 // Task is untied iff (Flags & 1) == 0.
2852 // Task is final iff (Flags & 2) == 2.
2853 // Task is not final iff (Flags & 2) == 0.
2854 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
2855 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
2856 // Task is detachable iff (Flags & 64) == 64.
2857 // Task is not detachable iff (Flags & 64) == 0.
2858 // Task is priority iff (Flags & 32) == 32.
2859 // Task is not priority iff (Flags & 32) == 0.
2860 // TODO: Handle the other flags.
2861 Value *Flags = Builder.getInt32(Tied);
2862 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
2863 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
2864 if (Final) {
2865 Value *FinalFlag =
2866 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
2867 Flags = Builder.CreateOr(FinalFlag, Flags);
2868 }
2869
2870 if (Mergeable || UseMergedIf0Path)
2871 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2872 if (EventHandle)
2873 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
2874 if (Priority)
2875 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2876
2877 // Argument - `sizeof_kmp_task_t` (TaskSize)
2878 // Tasksize refers to the size in bytes of kmp_task_t data structure
2879 // including private vars accessed in task.
2880 // TODO: add kmp_task_t_with_privates (privates)
2881 Value *TaskSize = Builder.getInt64(
2882 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2883
2884 // Argument - `sizeof_shareds` (SharedsSize)
2885 // SharedsSize refers to the shareds array size in the kmp_task_t data
2886 // structure.
2887 Value *SharedsSize = Builder.getInt64(0);
2888 if (HasShareds) {
2889 AllocaInst *ArgStructAlloca =
2891 assert(ArgStructAlloca &&
2892 "Unable to find the alloca instruction corresponding to arguments "
2893 "for extracted function");
2894 std::optional<TypeSize> ArgAllocSize =
2895 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2896 assert(ArgAllocSize &&
2897 "Unable to determine size of arguments for extracted function");
2898 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2899 }
2900 // Emit the @__kmpc_omp_task_alloc runtime call
2901 // The runtime call returns a pointer to an area where the task captured
2902 // variables must be copied before the task is run (TaskData)
2904 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2905 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2906 /*task_func=*/&OutlinedFn});
2907
2908 if (Affinities.Count && Affinities.Info) {
2910 OMPRTL___kmpc_omp_reg_task_with_affinity);
2911
2912 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
2913 Affinities.Count, Affinities.Info});
2914 }
2915
2916 // Emit detach clause initialization.
2917 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
2918 // task_descriptor);
2919 if (EventHandle) {
2921 OMPRTL___kmpc_task_allow_completion_event);
2922 llvm::Value *EventVal =
2923 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
2924 llvm::Value *EventHandleAddr =
2925 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
2926 Builder.getPtrTy(0));
2927 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
2928 Builder.CreateStore(EventVal, EventHandleAddr);
2929 }
2930 // Copy the arguments for outlined function
2931 if (HasShareds) {
2932 Value *Shareds = StaleCI->getArgOperand(1);
2933 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2934 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2935 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2936 SharedsSize);
2937 }
2938
2939 if (Priority) {
2940 //
2941 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
2942 // we populate the priority information into the "kmp_task_t" here
2943 //
2944 // The struct "kmp_task_t" definition is available in kmp.h
2945 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
2946 // data2 is used for priority
2947 //
2948 Type *Int32Ty = Builder.getInt32Ty();
2949 Constant *Zero = ConstantInt::get(Int32Ty, 0);
2950 // kmp_task_t* => { ptr }
2951 Type *TaskPtr = StructType::get(VoidPtr);
2952 Value *TaskGEP =
2953 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
2954 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
2955 Type *TaskStructType = StructType::get(
2956 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
2957 Value *PriorityData = Builder.CreateInBoundsGEP(
2958 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
2959 // kmp_cmplrdata_t => { ptr, ptr }
2960 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
2961 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
2962 PriorityData, {Zero, Zero});
2963 Builder.CreateStore(Priority, CmplrData);
2964 }
2965
2966 Value *DepArray = nullptr;
2967 Value *NumDeps = nullptr;
2968 if (Dependencies.DepArray) {
2969 DepArray = Dependencies.DepArray;
2970 NumDeps = Dependencies.NumDeps;
2971 } else if (!Dependencies.Deps.empty()) {
2972 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
2973 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2974 }
2975
2976 // In the presence of the `if` clause, the following IR is generated:
2977 // ...
2978 // %data = call @__kmpc_omp_task_alloc(...)
2979 // br i1 %if_condition, label %then, label %else
2980 // then:
2981 // call @__kmpc_omp_task(...)
2982 // br label %exit
2983 // else:
2984 // ;; Wait for resolution of dependencies, if any, before
2985 // ;; beginning the task
2986 // call @__kmpc_omp_wait_deps(...)
2987 // call @__kmpc_omp_task_begin_if0(...)
2988 // call @outlined_fn(...)
2989 // call @__kmpc_omp_task_complete_if0(...)
2990 // br label %exit
2991 // exit:
2992 // ...
2993 if (IfCondition && !UseMergedIf0Path) {
2994 // `SplitBlockAndInsertIfThenElse` requires the block to have a
2995 // terminator.
2996 splitBB(Builder, /*CreateBranch=*/true, "if.end");
2997 Instruction *IfTerminator =
2998 Builder.GetInsertPoint()->getParent()->getTerminator();
2999 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3000 Builder.SetInsertPoint(IfTerminator);
3001 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3002 &ElseTI);
3003 Builder.SetInsertPoint(ElseTI);
3004
3005 if (DepArray) {
3006 Function *TaskWaitFn =
3007 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3009 TaskWaitFn,
3010 {Ident, ThreadID, NumDeps, DepArray,
3011 ConstantInt::get(Builder.getInt32Ty(), 0),
3013 }
3014 Function *TaskBeginFn =
3015 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3016 Function *TaskCompleteFn =
3017 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3018 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3019 CallInst *CI = nullptr;
3020 if (HasShareds)
3021 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3022 else
3023 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3024 CI->setDebugLoc(StaleCI->getDebugLoc());
3025 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3026 Builder.SetInsertPoint(ThenTI);
3027 }
3028
3029 if (DepArray) {
3030 Function *TaskFn =
3031 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3033 TaskFn,
3034 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3035 ConstantInt::get(Builder.getInt32Ty(), 0),
3037
3038 } else {
3039 // Emit the @__kmpc_omp_task runtime call to spawn the task
3040 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3041 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3042 }
3043
3044 StaleCI->eraseFromParent();
3045
3046 Builder.SetInsertPoint(TaskAllocaBB, TaskAllocaBB->begin());
3047 if (HasShareds) {
3048 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3049 OutlinedFn.getArg(1)->replaceUsesWithIf(
3050 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3051 }
3052
3053 for (Instruction *I : llvm::reverse(ToBeDeleted))
3054 I->eraseFromParent();
3055 };
3056
3057 addOutlineInfo(std::move(OI));
3058 Builder.SetInsertPoint(TaskExitBB, TaskExitBB->begin());
3059
3060 return Builder.saveIP();
3061}
3062
3064 const LocationDescription &Loc, InsertPointTy AllocaIP,
3065 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3066 if (!updateToLocation(Loc))
3067 return InsertPointTy();
3068
3069 uint32_t SrcLocStrSize;
3070 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3071 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3072 Value *ThreadID = getOrCreateThreadID(Ident);
3073
3074 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3075 Function *TaskgroupFn =
3076 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3077 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3078
3079 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3080 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3081 return Err;
3082
3083 Builder.SetInsertPoint(TaskgroupExitBB);
3084 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3085 Function *EndTaskgroupFn =
3086 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3087 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3088
3089 return Builder.saveIP();
3090}
3091
3093 const LocationDescription &Loc, InsertPointTy AllocaIP,
3095 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3096 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3097
3098 if (!updateToLocation(Loc))
3099 return Loc.IP;
3100
3101 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3102
3103 // Each section is emitted as a switch case
3104 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3105 // -> OMP.createSection() which generates the IR for each section
3106 // Iterate through all sections and emit a switch construct:
3107 // switch (IV) {
3108 // case 0:
3109 // <SectionStmt[0]>;
3110 // break;
3111 // ...
3112 // case <NumSection> - 1:
3113 // <SectionStmt[<NumSection> - 1]>;
3114 // break;
3115 // }
3116 // ...
3117 // section_loop.after:
3118 // <FiniCB>;
3119 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3120 Builder.restoreIP(CodeGenIP);
3122 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3123 Function *CurFn = Continue->getParent();
3124 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3125
3126 unsigned CaseNumber = 0;
3127 for (auto SectionCB : SectionCBs) {
3129 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3130 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3131 Builder.SetInsertPoint(CaseBB);
3132 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3133 if (Error Err =
3134 SectionCB(InsertPointTy(),
3135 {CaseEndBr->getParent(), CaseEndBr->getIterator()}, {}))
3136 return Err;
3137 CaseNumber++;
3138 }
3139 // remove the existing terminator from body BB since there can be no
3140 // terminators after switch/case
3141 return Error::success();
3142 };
3143 // Loop body ends here
3144 // LowerBound, UpperBound, and STride for createCanonicalLoop
3145 Type *I32Ty = Type::getInt32Ty(M.getContext());
3146 Value *LB = ConstantInt::get(I32Ty, 0);
3147 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3148 Value *ST = ConstantInt::get(I32Ty, 1);
3150 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3151 if (!LoopInfo)
3152 return LoopInfo.takeError();
3153
3154 InsertPointOrErrorTy WsloopIP =
3155 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3156 WorksharingLoopType::ForStaticLoop, !IsNowait);
3157 if (!WsloopIP)
3158 return WsloopIP.takeError();
3159 InsertPointTy AfterIP = *WsloopIP;
3160
3161 BasicBlock *LoopFini = AfterIP.getBlock()->getSinglePredecessor();
3162 assert(LoopFini && "Bad structure of static workshare loop finalization");
3163
3164 // Apply the finalization callback in LoopAfterBB
3165 auto FiniInfo = FinalizationStack.pop_back_val();
3166 assert(FiniInfo.DK == OMPD_sections &&
3167 "Unexpected finalization stack state!");
3168 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3169 return Err;
3170
3171 return AfterIP;
3172}
3173
3176 BodyGenCallbackTy BodyGenCB,
3177 FinalizeCallbackTy FiniCB) {
3178 if (!updateToLocation(Loc))
3179 return Loc.IP;
3180
3181 auto FiniCBWrapper = [&](InsertPointTy IP) {
3182 if (IP.getBlock()->end() != IP.getPoint())
3183 return FiniCB(IP);
3184 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3185 // will fail because that function requires the Finalization Basic Block to
3186 // have a terminator, which is already removed by EmitOMPRegionBody.
3187 // IP is currently at cancelation block.
3188 // We need to backtrack to the condition block to fetch
3189 // the exit block and create a branch from cancelation
3190 // to exit block.
3192 Builder.restoreIP(IP);
3193 auto *CaseBB = Loc.IP.getBlock();
3194 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3195 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3196 Instruction *I = Builder.CreateBr(ExitBB);
3197 IP = InsertPointTy(I->getParent(), I->getIterator());
3198 return FiniCB(IP);
3199 };
3200
3201 Directive OMPD = Directive::OMPD_sections;
3202 // Since we are using Finalization Callback here, HasFinalize
3203 // and IsCancellable have to be true
3204 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3205 /*Conditional*/ false, /*hasFinalize*/ true,
3206 /*IsCancellable*/ true);
3207}
3208
3214
3215Value *OpenMPIRBuilder::getGPUThreadID() {
3218 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3219 {});
3220}
3221
3222Value *OpenMPIRBuilder::getGPUWarpSize() {
3224 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3225}
3226
3227Value *OpenMPIRBuilder::getNVPTXWarpID() {
3228 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3229 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3230}
3231
3232Value *OpenMPIRBuilder::getNVPTXLaneID() {
3233 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3234 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3235 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3236 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3237 "nvptx_lane_id");
3238}
3239
3240Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3241 Type *ToType) {
3242 Type *FromType = From->getType();
3243 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3244 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3245 assert(FromSize > 0 && "From size must be greater than zero");
3246 assert(ToSize > 0 && "To size must be greater than zero");
3247 if (FromType == ToType)
3248 return From;
3249 if (FromSize == ToSize)
3250 return Builder.CreateBitCast(From, ToType);
3251 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3252 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3253 InsertPointTy SaveIP = Builder.saveIP();
3254 Builder.restoreIP(AllocaIP);
3255 Value *CastItem = Builder.CreateAlloca(ToType);
3256 Builder.restoreIP(SaveIP);
3257
3258 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3259 CastItem, Builder.getPtrTy(0));
3260 Builder.CreateStore(From, ValCastItem);
3261 return Builder.CreateLoad(ToType, CastItem);
3262}
3263
3264Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3265 Value *Element,
3266 Type *ElementType,
3267 Value *Offset) {
3268 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3269 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3270
3271 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3272 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3273 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3274 Value *WarpSize =
3275 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3277 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3278 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3279 Value *WarpSizeCast =
3280 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3281 Value *ShuffleCall =
3282 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3283 return castValueToType(AllocaIP, ShuffleCall, CastTy);
3284}
3285
3286void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3287 Value *DstAddr, Type *ElemType,
3288 Value *Offset, Type *ReductionArrayTy,
3289 bool IsByRefElem) {
3290 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3291 // Create the loop over the big sized data.
3292 // ptr = (void*)Elem;
3293 // ptrEnd = (void*) Elem + 1;
3294 // Step = 8;
3295 // while (ptr + Step < ptrEnd)
3296 // shuffle((int64_t)*ptr);
3297 // Step = 4;
3298 // while (ptr + Step < ptrEnd)
3299 // shuffle((int32_t)*ptr);
3300 // ...
3301 Type *IndexTy = Builder.getIndexTy(
3302 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3303 Value *ElemPtr = DstAddr;
3304 Value *Ptr = SrcAddr;
3305 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3306 if (Size < IntSize)
3307 continue;
3308 Type *IntType = Builder.getIntNTy(IntSize * 8);
3309 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3310 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3311 Value *SrcAddrGEP =
3312 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3313 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3314 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3315
3316 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3317 if ((Size / IntSize) > 1) {
3318 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3319 SrcAddrGEP, Builder.getPtrTy());
3320 BasicBlock *PreCondBB =
3321 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3322 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3323 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3324 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3325 emitBlock(PreCondBB, CurFunc);
3326 PHINode *PhiSrc =
3327 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3328 PhiSrc->addIncoming(Ptr, CurrentBB);
3329 PHINode *PhiDest =
3330 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3331 PhiDest->addIncoming(ElemPtr, CurrentBB);
3332 Ptr = PhiSrc;
3333 ElemPtr = PhiDest;
3334 Value *PtrDiff = Builder.CreatePtrDiff(
3335 Builder.getInt8Ty(), PtrEnd,
3336 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3337 Builder.CreateCondBr(
3338 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3339 ExitBB);
3340 emitBlock(ThenBB, CurFunc);
3341 Value *Res = createRuntimeShuffleFunction(
3342 AllocaIP,
3343 Builder.CreateAlignedLoad(
3344 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3345 IntType, Offset);
3346 Builder.CreateAlignedStore(Res, ElemPtr,
3347 M.getDataLayout().getPrefTypeAlign(ElemType));
3348 Value *LocalPtr =
3349 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3350 Value *LocalElemPtr =
3351 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3352 PhiSrc->addIncoming(LocalPtr, ThenBB);
3353 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3354 emitBranch(PreCondBB);
3355 emitBlock(ExitBB, CurFunc);
3356 } else {
3357 Value *Res = createRuntimeShuffleFunction(
3358 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3359 if (ElemType->isIntegerTy() && ElemType->getScalarSizeInBits() <
3360 Res->getType()->getScalarSizeInBits())
3361 Res = Builder.CreateTrunc(Res, ElemType);
3362 Builder.CreateStore(Res, ElemPtr);
3363 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3364 ElemPtr =
3365 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3366 }
3367 Size = Size % IntSize;
3368 }
3369}
3370
3371Error OpenMPIRBuilder::emitReductionListCopy(
3372 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3373 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3374 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3375 Type *IndexTy = Builder.getIndexTy(
3376 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3377 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3378
3379 // Iterates, element-by-element, through the source Reduce list and
3380 // make a copy.
3381 for (auto En : enumerate(ReductionInfos)) {
3382 const ReductionInfo &RI = En.value();
3383 Value *SrcElementAddr = nullptr;
3384 AllocaInst *DestAlloca = nullptr;
3385 Value *DestElementAddr = nullptr;
3386 Value *DestElementPtrAddr = nullptr;
3387 // Should we shuffle in an element from a remote lane?
3388 bool ShuffleInElement = false;
3389 // Set to true to update the pointer in the dest Reduce list to a
3390 // newly created element.
3391 bool UpdateDestListPtr = false;
3392
3393 // Step 1.1: Get the address for the src element in the Reduce list.
3394 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3395 ReductionArrayTy, SrcBase,
3396 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3397 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3398
3399 // Step 1.2: Create a temporary to store the element in the destination
3400 // Reduce list.
3401 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3402 ReductionArrayTy, DestBase,
3403 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3404 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3405 switch (Action) {
3407 InsertPointTy CurIP = Builder.saveIP();
3408 Builder.restoreIP(AllocaIP);
3409
3410 Type *DestAllocaType =
3411 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3412 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3413 ".omp.reduction.element");
3414 DestAlloca->setAlignment(
3415 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3416 DestElementAddr = DestAlloca;
3417 DestElementAddr =
3418 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3419 DestElementAddr->getName() + ".ascast");
3420 Builder.restoreIP(CurIP);
3421 ShuffleInElement = true;
3422 UpdateDestListPtr = true;
3423 break;
3424 }
3426 DestElementAddr =
3427 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3428 break;
3429 }
3430 }
3431
3432 // Now that all active lanes have read the element in the
3433 // Reduce list, shuffle over the value from the remote lane.
3434 if (ShuffleInElement) {
3435 Type *ShuffleType = RI.ElementType;
3436 Value *ShuffleSrcAddr = SrcElementAddr;
3437 Value *ShuffleDestAddr = DestElementAddr;
3438 AllocaInst *LocalStorage = nullptr;
3439
3440 if (IsByRefElem) {
3441 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3442 assert(RI.ByRefAllocatedType &&
3443 "Expected by-ref allocated type to be set");
3444 // For by-ref reductions, we need to copy from the remote lane the
3445 // actual value of the partial reduction computed by that remote lane;
3446 // rather than, for example, a pointer to that data or, even worse, a
3447 // pointer to the descriptor of the by-ref reduction element.
3448 ShuffleType = RI.ByRefElementType;
3449
3450 if (RI.DataPtrPtrGen) {
3451 // Descriptor-based by-ref: extract data pointer from descriptor.
3452 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3453 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3454
3455 if (!GenResult)
3456 return GenResult.takeError();
3457
3458 ShuffleSrcAddr =
3459 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3460
3461 {
3462 InsertPointTy OldIP = Builder.saveIP();
3463 Builder.restoreIP(AllocaIP);
3464
3465 LocalStorage = Builder.CreateAlloca(ShuffleType);
3466 Builder.restoreIP(OldIP);
3467 ShuffleDestAddr = LocalStorage;
3468 }
3469 } else {
3470 // Non-descriptor by-ref: the pointer already references data
3471 // directly. Shuffle into the destination alloca.
3472 ShuffleDestAddr = DestElementAddr;
3473 }
3474 }
3475
3476 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3477 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3478
3479 if (IsByRefElem && RI.DataPtrPtrGen) {
3480 // Copy descriptor from source and update base_ptr to shuffled data
3481 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3482 DestAlloca, Builder.getPtrTy(), ".ascast");
3483
3484 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3485 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3486 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3487
3488 if (!GenResult)
3489 return GenResult.takeError();
3490 }
3491 } else {
3492 switch (RI.EvaluationKind) {
3493 case EvalKind::Scalar: {
3494 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3495 // Store the source element value to the dest element address.
3496 Builder.CreateStore(Elem, DestElementAddr);
3497 break;
3498 }
3499 case EvalKind::Complex: {
3500 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3501 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3502 Value *SrcReal = Builder.CreateLoad(
3503 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3504 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3505 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3506 Value *SrcImg = Builder.CreateLoad(
3507 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3508
3509 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3510 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3511 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3512 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3513 Builder.CreateStore(SrcReal, DestRealPtr);
3514 Builder.CreateStore(SrcImg, DestImgPtr);
3515 break;
3516 }
3517 case EvalKind::Aggregate: {
3518 Value *SizeVal = Builder.getInt64(
3519 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3520 Builder.CreateMemCpy(
3521 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3522 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3523 SizeVal, false);
3524 break;
3525 }
3526 };
3527 }
3528
3529 // Step 3.1: Modify reference in dest Reduce list as needed.
3530 // Modifying the reference in Reduce list to point to the newly
3531 // created element. The element is live in the current function
3532 // scope and that of functions it invokes (i.e., reduce_function).
3533 // RemoteReduceData[i] = (void*)&RemoteElem
3534 if (UpdateDestListPtr) {
3535 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3536 DestElementAddr, Builder.getPtrTy(),
3537 DestElementAddr->getName() + ".ascast");
3538 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3539 }
3540 }
3541
3542 return Error::success();
3543}
3544
3545Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3546 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3547 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3548 IRBuilder<>::InsertPointGuard IPG(Builder);
3549 LLVMContext &Ctx = M.getContext();
3550 FunctionType *FuncTy = FunctionType::get(
3551 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3552 /* IsVarArg */ false);
3553 Function *WcFunc =
3555 "_omp_reduction_inter_warp_copy_func", &M);
3556 WcFunc->setCallingConv(Config.getRuntimeCC());
3557 WcFunc->setAttributes(FuncAttrs);
3558 WcFunc->addParamAttr(0, Attribute::NoUndef);
3559 WcFunc->addParamAttr(1, Attribute::NoUndef);
3560 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3561 Builder.SetInsertPoint(EntryBB);
3562 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3563
3564 // ReduceList: thread local Reduce list.
3565 // At the stage of the computation when this function is called, partially
3566 // aggregated values reside in the first lane of every active warp.
3567 Argument *ReduceListArg = WcFunc->getArg(0);
3568 // NumWarps: number of warps active in the parallel region. This could
3569 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3570 Argument *NumWarpsArg = WcFunc->getArg(1);
3571
3572 // This array is used as a medium to transfer, one reduce element at a time,
3573 // the data from the first lane of every warp to lanes in the first warp
3574 // in order to perform the final step of a reduction in a parallel region
3575 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3576 // for reduced latency, as well as to have a distinct copy for concurrently
3577 // executing target regions. The array is declared with common linkage so
3578 // as to be shared across compilation units.
3579 StringRef TransferMediumName =
3580 "__openmp_nvptx_data_transfer_temporary_storage";
3581 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3582 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3583 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3584 if (!TransferMedium) {
3585 TransferMedium = new GlobalVariable(
3586 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3587 UndefValue::get(ArrayTy), TransferMediumName,
3588 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3589 /*AddressSpace=*/3);
3590 }
3591
3592 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3593 Value *GPUThreadID = getGPUThreadID();
3594 // nvptx_lane_id = nvptx_id % warpsize
3595 Value *LaneID = getNVPTXLaneID();
3596 // nvptx_warp_id = nvptx_id / warpsize
3597 Value *WarpID = getNVPTXWarpID();
3598
3599 InsertPointTy AllocaIP =
3600 InsertPointTy(Builder.GetInsertBlock(),
3601 Builder.GetInsertBlock()->getFirstInsertionPt());
3602 Type *Arg0Type = ReduceListArg->getType();
3603 Type *Arg1Type = NumWarpsArg->getType();
3604 Builder.restoreIP(AllocaIP);
3605 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3606 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3607 AllocaInst *NumWarpsAlloca =
3608 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3609 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3610 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3611 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3612 NumWarpsAlloca, Builder.getPtrTy(0),
3613 NumWarpsAlloca->getName() + ".ascast");
3614 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3615 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3616 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3617 InsertPointTy CodeGenIP =
3618 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3619 Builder.restoreIP(CodeGenIP);
3620
3621 Value *ReduceList =
3622 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3623
3624 for (auto En : enumerate(ReductionInfos)) {
3625 //
3626 // Warp master copies reduce element to transfer medium in __shared__
3627 // memory.
3628 //
3629 const ReductionInfo &RI = En.value();
3630 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3631 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3632 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3633 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3634 Type *CType = Builder.getIntNTy(TySize * 8);
3635
3636 unsigned NumIters = RealTySize / TySize;
3637 if (NumIters == 0)
3638 continue;
3639 Value *Cnt = nullptr;
3640 Value *CntAddr = nullptr;
3641 BasicBlock *PrecondBB = nullptr;
3642 BasicBlock *ExitBB = nullptr;
3643 if (NumIters > 1) {
3644 CodeGenIP = Builder.saveIP();
3645 Builder.restoreIP(AllocaIP);
3646 CntAddr =
3647 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3648
3649 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3650 CntAddr->getName() + ".ascast");
3651 Builder.restoreIP(CodeGenIP);
3652 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3653 CntAddr,
3654 /*Volatile=*/false);
3655 PrecondBB = BasicBlock::Create(Ctx, "precond");
3656 ExitBB = BasicBlock::Create(Ctx, "exit");
3657 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3658 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3659 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3660 /*Volatile=*/false);
3661 Value *Cmp = Builder.CreateICmpULT(
3662 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3663 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3664 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3665 }
3666
3667 // kmpc_barrier.
3668 InsertPointOrErrorTy BarrierIP1 =
3670 omp::Directive::OMPD_unknown,
3671 /* ForceSimpleCall */ false,
3672 /* CheckCancelFlag */ true);
3673 if (!BarrierIP1)
3674 return BarrierIP1.takeError();
3675 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3676 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3677 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3678
3679 // if (lane_id == 0)
3680 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3681 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3682 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3683
3684 // Reduce element = LocalReduceList[i]
3685 auto *RedListArrayTy =
3686 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3687 Type *IndexTy = Builder.getIndexTy(
3688 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3689 Value *ElemPtrPtr =
3690 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3691 {ConstantInt::get(IndexTy, 0),
3692 ConstantInt::get(IndexTy, En.index())});
3693 // elemptr = ((CopyType*)(elemptrptr)) + I
3694 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3695
3696 if (IsByRefElem && RI.DataPtrPtrGen) {
3697 InsertPointOrErrorTy GenRes =
3698 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3699
3700 if (!GenRes)
3701 return GenRes.takeError();
3702
3703 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3704 }
3705
3706 if (NumIters > 1)
3707 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3708
3709 // Get pointer to location in transfer medium.
3710 // MediumPtr = &medium[warp_id]
3711 Value *MediumPtr = Builder.CreateInBoundsGEP(
3712 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3713 // elem = *elemptr
3714 //*MediumPtr = elem
3715 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3716 // Store the source element value to the dest element address.
3717 Builder.CreateStore(Elem, MediumPtr,
3718 /*IsVolatile*/ true);
3719 Builder.CreateBr(MergeBB);
3720
3721 // else
3722 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3723 Builder.CreateBr(MergeBB);
3724
3725 // endif
3726 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3727 InsertPointOrErrorTy BarrierIP2 =
3729 omp::Directive::OMPD_unknown,
3730 /* ForceSimpleCall */ false,
3731 /* CheckCancelFlag */ true);
3732 if (!BarrierIP2)
3733 return BarrierIP2.takeError();
3734
3735 // Warp 0 copies reduce element from transfer medium
3736 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3737 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3738 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3739
3740 Value *NumWarpsVal =
3741 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3742 // Up to 32 threads in warp 0 are active.
3743 Value *IsActiveThread =
3744 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3745 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3746
3747 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3748
3749 // SecMediumPtr = &medium[tid]
3750 // SrcMediumVal = *SrcMediumPtr
3751 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3752 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3753 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3754 Value *TargetElemPtrPtr =
3755 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3756 {ConstantInt::get(IndexTy, 0),
3757 ConstantInt::get(IndexTy, En.index())});
3758 Value *TargetElemPtrVal =
3759 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3760 Value *TargetElemPtr = TargetElemPtrVal;
3761
3762 if (IsByRefElem && RI.DataPtrPtrGen) {
3763 InsertPointOrErrorTy GenRes =
3764 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3765
3766 if (!GenRes)
3767 return GenRes.takeError();
3768
3769 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3770 }
3771
3772 if (NumIters > 1)
3773 TargetElemPtr =
3774 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3775
3776 // *TargetElemPtr = SrcMediumVal;
3777 Value *SrcMediumValue =
3778 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3779 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3780 Builder.CreateBr(W0MergeBB);
3781
3782 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3783 Builder.CreateBr(W0MergeBB);
3784
3785 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3786
3787 if (NumIters > 1) {
3788 Cnt = Builder.CreateNSWAdd(
3789 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3790 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3791
3792 auto *CurFn = Builder.GetInsertBlock()->getParent();
3793 emitBranch(PrecondBB);
3794 emitBlock(ExitBB, CurFn);
3795 }
3796 RealTySize %= TySize;
3797 }
3798 }
3799
3800 Builder.CreateRetVoid();
3801
3802 return WcFunc;
3803}
3804
3805Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3806 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3807 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3808 LLVMContext &Ctx = M.getContext();
3809 IRBuilder<>::InsertPointGuard IPG(Builder);
3810 FunctionType *FuncTy =
3811 FunctionType::get(Builder.getVoidTy(),
3812 {Builder.getPtrTy(), Builder.getInt16Ty(),
3813 Builder.getInt16Ty(), Builder.getInt16Ty()},
3814 /* IsVarArg */ false);
3815 Function *SarFunc =
3817 "_omp_reduction_shuffle_and_reduce_func", &M);
3818 SarFunc->setCallingConv(Config.getRuntimeCC());
3819 SarFunc->setAttributes(FuncAttrs);
3820 SarFunc->addParamAttr(0, Attribute::NoUndef);
3821 SarFunc->addParamAttr(1, Attribute::NoUndef);
3822 SarFunc->addParamAttr(2, Attribute::NoUndef);
3823 SarFunc->addParamAttr(3, Attribute::NoUndef);
3824 SarFunc->addParamAttr(1, Attribute::SExt);
3825 SarFunc->addParamAttr(2, Attribute::SExt);
3826 SarFunc->addParamAttr(3, Attribute::SExt);
3827 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3828 Builder.SetInsertPoint(EntryBB);
3829 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3830
3831 // Thread local Reduce list used to host the values of data to be reduced.
3832 Argument *ReduceListArg = SarFunc->getArg(0);
3833 // Current lane id; could be logical.
3834 Argument *LaneIDArg = SarFunc->getArg(1);
3835 // Offset of the remote source lane relative to the current lane.
3836 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
3837 // Algorithm version. This is expected to be known at compile time.
3838 Argument *AlgoVerArg = SarFunc->getArg(3);
3839
3840 Type *ReduceListArgType = ReduceListArg->getType();
3841 Type *LaneIDArgType = LaneIDArg->getType();
3842 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
3843 Value *ReduceListAlloca = Builder.CreateAlloca(
3844 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
3845 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3846 LaneIDArg->getName() + ".addr");
3847 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
3848 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
3849 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3850 AlgoVerArg->getName() + ".addr");
3851 ArrayType *RedListArrayTy =
3852 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3853
3854 // Create a local thread-private variable to host the Reduce list
3855 // from a remote lane.
3856 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
3857 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
3858
3859 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3860 ReduceListAlloca, ReduceListArgType,
3861 ReduceListAlloca->getName() + ".ascast");
3862 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3863 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
3864 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3865 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
3866 RemoteLaneOffsetAlloca->getName() + ".ascast");
3867 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3868 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
3869 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3870 RemoteReductionListAlloca, Builder.getPtrTy(),
3871 RemoteReductionListAlloca->getName() + ".ascast");
3872
3873 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3874 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
3875 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
3876 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
3877
3878 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
3879 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
3880 Value *RemoteLaneOffset =
3881 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
3882 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
3883
3884 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
3885
3886 // This loop iterates through the list of reduce elements and copies,
3887 // element by element, from a remote lane in the warp to RemoteReduceList,
3888 // hosted on the thread's stack.
3889 Error EmitRedLsCpRes = emitReductionListCopy(
3890 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
3891 ReduceList, RemoteListAddrCast, IsByRef,
3892 {RemoteLaneOffset, nullptr, nullptr});
3893
3894 if (EmitRedLsCpRes)
3895 return EmitRedLsCpRes;
3896
3897 // The actions to be performed on the Remote Reduce list is dependent
3898 // on the algorithm version.
3899 //
3900 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
3901 // LaneId % 2 == 0 && Offset > 0):
3902 // do the reduction value aggregation
3903 //
3904 // The thread local variable Reduce list is mutated in place to host the
3905 // reduced data, which is the aggregated value produced from local and
3906 // remote lanes.
3907 //
3908 // Note that AlgoVer is expected to be a constant integer known at compile
3909 // time.
3910 // When AlgoVer==0, the first conjunction evaluates to true, making
3911 // the entire predicate true during compile time.
3912 // When AlgoVer==1, the second conjunction has only the second part to be
3913 // evaluated during runtime. Other conjunctions evaluates to false
3914 // during compile time.
3915 // When AlgoVer==2, the third conjunction has only the second part to be
3916 // evaluated during runtime. Other conjunctions evaluates to false
3917 // during compile time.
3918 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
3919 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
3920 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
3921 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
3922 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
3923 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
3924 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
3925 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
3926 Value *RemoteOffsetComp =
3927 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
3928 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
3929 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
3930 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
3931
3932 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3933 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3934 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3935
3936 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
3937 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3938 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3939 ReduceList, Builder.getPtrTy());
3940 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3941 RemoteListAddrCast, Builder.getPtrTy());
3942 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
3943 ->addFnAttr(Attribute::NoUnwind);
3944 Builder.CreateBr(MergeBB);
3945
3946 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3947 Builder.CreateBr(MergeBB);
3948
3949 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3950
3951 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
3952 // Reduce list.
3953 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
3954 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
3955 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
3956
3957 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
3958 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
3959 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
3960 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
3961
3962 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
3963
3964 EmitRedLsCpRes = emitReductionListCopy(
3965 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
3966 RemoteListAddrCast, ReduceList, IsByRef);
3967
3968 if (EmitRedLsCpRes)
3969 return EmitRedLsCpRes;
3970
3971 Builder.CreateBr(CpyMergeBB);
3972
3973 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
3974 Builder.CreateBr(CpyMergeBB);
3975
3976 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
3977
3978 Builder.CreateRetVoid();
3979
3980 return SarFunc;
3981}
3982
3984OpenMPIRBuilder::generateReductionDescriptor(
3985 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
3986 Type *DescriptorType,
3987 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
3988 DataPtrPtrGen) {
3989
3990 // Copy the source descriptor to preserve all metadata (rank, extents,
3991 // strides, etc.)
3992 Value *DescriptorSize =
3993 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
3994 Builder.CreateMemCpy(
3995 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
3996 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
3997 DescriptorSize);
3998
3999 // Update the base pointer field to point to the local shuffled data
4000 Value *DataPtrField;
4001 InsertPointOrErrorTy GenResult =
4002 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4003
4004 if (!GenResult)
4005 return GenResult.takeError();
4006
4007 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4008 DataPtr, Builder.getPtrTy(), ".ascast"),
4009 DataPtrField);
4010
4011 return Builder.saveIP();
4012}
4013
4014Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4015 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4016 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4017 InsertPointTy OldIP = Builder.saveIP();
4018 Builder.restoreIP(AllocaIP);
4019
4020 AllocaInst *DescriptorAlloca =
4021 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4022 DescriptorAlloca->setAlignment(
4023 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4024 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4025 DescriptorAlloca, DescriptorPtrTy,
4026 DescriptorAlloca->getName() + ".ascast");
4027
4028 Builder.restoreIP(OldIP);
4029
4030 InsertPointOrErrorTy GenResult =
4031 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4032 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4033 if (!GenResult)
4034 return GenResult.takeError();
4035
4036 return DescriptorAddr;
4037}
4038
4039Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4040 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4041 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4042 IRBuilder<>::InsertPointGuard IPG(Builder);
4043 LLVMContext &Ctx = M.getContext();
4044 FunctionType *FuncTy = FunctionType::get(
4045 Builder.getVoidTy(),
4046 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4047 /* IsVarArg */ false);
4048 Function *LtGCFunc =
4050 "_omp_reduction_list_to_global_copy_func", &M);
4051 LtGCFunc->setAttributes(FuncAttrs);
4052 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4053 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4054 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4055
4056 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4057 Builder.SetInsertPoint(EntryBlock);
4058 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4059
4060 // Buffer: global reduction buffer.
4061 Argument *BufferArg = LtGCFunc->getArg(0);
4062 // Idx: index of the buffer.
4063 Argument *IdxArg = LtGCFunc->getArg(1);
4064 // ReduceList: thread local Reduce list.
4065 Argument *ReduceListArg = LtGCFunc->getArg(2);
4066
4067 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4068 BufferArg->getName() + ".addr");
4069 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4070 IdxArg->getName() + ".addr");
4071 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4072 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4073 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4074 BufferArgAlloca, Builder.getPtrTy(),
4075 BufferArgAlloca->getName() + ".ascast");
4076 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4077 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4078 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4079 ReduceListArgAlloca, Builder.getPtrTy(),
4080 ReduceListArgAlloca->getName() + ".ascast");
4081
4082 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4083 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4084 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4085
4086 Value *LocalReduceList =
4087 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4088 Value *BufferArgVal =
4089 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4090 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4091 Type *IndexTy = Builder.getIndexTy(
4092 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4093 for (auto En : enumerate(ReductionInfos)) {
4094 const ReductionInfo &RI = En.value();
4095 auto *RedListArrayTy =
4096 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4097 // Reduce element = LocalReduceList[i]
4098 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4099 RedListArrayTy, LocalReduceList,
4100 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4101 // elemptr = ((CopyType*)(elemptrptr)) + I
4102 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4103
4104 // Global = Buffer.VD[Idx];
4105 Value *BufferVD =
4106 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4107 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4108 ReductionsBufferTy, BufferVD, 0, En.index());
4109
4110 switch (RI.EvaluationKind) {
4111 case EvalKind::Scalar: {
4112 Value *TargetElement;
4113
4114 if (IsByRef.empty() || !IsByRef[En.index()]) {
4115 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4116 } else {
4117 if (RI.DataPtrPtrGen) {
4118 InsertPointOrErrorTy GenResult =
4119 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4120
4121 if (!GenResult)
4122 return GenResult.takeError();
4123
4124 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4125 }
4126 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4127 }
4128
4129 Builder.CreateStore(TargetElement, GlobVal);
4130 break;
4131 }
4132 case EvalKind::Complex: {
4133 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4134 RI.ElementType, ElemPtr, 0, 0, ".realp");
4135 Value *SrcReal = Builder.CreateLoad(
4136 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4137 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4138 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4139 Value *SrcImg = Builder.CreateLoad(
4140 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4141
4142 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4143 RI.ElementType, GlobVal, 0, 0, ".realp");
4144 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4145 RI.ElementType, GlobVal, 0, 1, ".imagp");
4146 Builder.CreateStore(SrcReal, DestRealPtr);
4147 Builder.CreateStore(SrcImg, DestImgPtr);
4148 break;
4149 }
4150 case EvalKind::Aggregate: {
4151 Value *SizeVal =
4152 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4153 Builder.CreateMemCpy(
4154 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4155 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4156 break;
4157 }
4158 }
4159 }
4160
4161 Builder.CreateRetVoid();
4162 return LtGCFunc;
4163}
4164
4165Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4166 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4167 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4168 IRBuilder<>::InsertPointGuard IPG(Builder);
4169 LLVMContext &Ctx = M.getContext();
4170 FunctionType *FuncTy = FunctionType::get(
4171 Builder.getVoidTy(),
4172 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4173 /* IsVarArg */ false);
4174 Function *LtGRFunc =
4176 "_omp_reduction_list_to_global_reduce_func", &M);
4177 LtGRFunc->setAttributes(FuncAttrs);
4178 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4179 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4180 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4181
4182 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4183 Builder.SetInsertPoint(EntryBlock);
4184 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4185
4186 // Buffer: global reduction buffer.
4187 Argument *BufferArg = LtGRFunc->getArg(0);
4188 // Idx: index of the buffer.
4189 Argument *IdxArg = LtGRFunc->getArg(1);
4190 // ReduceList: thread local Reduce list.
4191 Argument *ReduceListArg = LtGRFunc->getArg(2);
4192
4193 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4194 BufferArg->getName() + ".addr");
4195 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4196 IdxArg->getName() + ".addr");
4197 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4198 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4199 auto *RedListArrayTy =
4200 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4201
4202 // 1. Build a list of reduction variables.
4203 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4204 Value *LocalReduceList =
4205 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4206
4207 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4208
4209 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4210 BufferArgAlloca, Builder.getPtrTy(),
4211 BufferArgAlloca->getName() + ".ascast");
4212 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4213 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4214 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4215 ReduceListArgAlloca, Builder.getPtrTy(),
4216 ReduceListArgAlloca->getName() + ".ascast");
4217 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4218 LocalReduceList, Builder.getPtrTy(),
4219 LocalReduceList->getName() + ".ascast");
4220
4221 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4222 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4223 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4224
4225 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4226 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4227 Type *IndexTy = Builder.getIndexTy(
4228 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4229 for (auto En : enumerate(ReductionInfos)) {
4230 const ReductionInfo &RI = En.value();
4231
4232 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4233 RedListArrayTy, LocalReduceListAddrCast,
4234 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4235 Value *BufferVD =
4236 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4237 // Global = Buffer.VD[Idx];
4238 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4239 ReductionsBufferTy, BufferVD, 0, En.index());
4240
4241 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4242 // Get source descriptor from the reduce list argument
4243 Value *ReduceList =
4244 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4245 Value *SrcElementPtrPtr =
4246 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4247 {ConstantInt::get(IndexTy, 0),
4248 ConstantInt::get(IndexTy, En.index())});
4249 Value *SrcDescriptorAddr =
4250 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4251
4252 // Copy descriptor from source and update base_ptr to global buffer data
4253 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4254 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4255 if (!ByRefAlloc)
4256 return ByRefAlloc.takeError();
4257
4258 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4259 } else {
4260 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4261 }
4262 }
4263
4264 // Call reduce_function(GlobalReduceList, ReduceList)
4265 Value *ReduceList =
4266 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4267 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4268 ->addFnAttr(Attribute::NoUnwind);
4269 Builder.CreateRetVoid();
4270 return LtGRFunc;
4271}
4272
4273Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4274 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4275 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4276 IRBuilder<>::InsertPointGuard IPG(Builder);
4277 LLVMContext &Ctx = M.getContext();
4278 FunctionType *FuncTy = FunctionType::get(
4279 Builder.getVoidTy(),
4280 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4281 /* IsVarArg */ false);
4282 Function *GtLCFunc =
4284 "_omp_reduction_global_to_list_copy_func", &M);
4285 GtLCFunc->setAttributes(FuncAttrs);
4286 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4287 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4288 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4289
4290 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4291 Builder.SetInsertPoint(EntryBlock);
4292 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4293
4294 // Buffer: global reduction buffer.
4295 Argument *BufferArg = GtLCFunc->getArg(0);
4296 // Idx: index of the buffer.
4297 Argument *IdxArg = GtLCFunc->getArg(1);
4298 // ReduceList: thread local Reduce list.
4299 Argument *ReduceListArg = GtLCFunc->getArg(2);
4300
4301 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4302 BufferArg->getName() + ".addr");
4303 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4304 IdxArg->getName() + ".addr");
4305 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4306 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4307 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4308 BufferArgAlloca, Builder.getPtrTy(),
4309 BufferArgAlloca->getName() + ".ascast");
4310 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4311 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4312 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4313 ReduceListArgAlloca, Builder.getPtrTy(),
4314 ReduceListArgAlloca->getName() + ".ascast");
4315 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4316 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4317 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4318
4319 Value *LocalReduceList =
4320 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4321 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4322 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4323 Type *IndexTy = Builder.getIndexTy(
4324 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4325 for (auto En : enumerate(ReductionInfos)) {
4326 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4327 auto *RedListArrayTy =
4328 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4329 // Reduce element = LocalReduceList[i]
4330 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4331 RedListArrayTy, LocalReduceList,
4332 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4333 // elemptr = ((CopyType*)(elemptrptr)) + I
4334 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4335 // Global = Buffer.VD[Idx];
4336 Value *BufferVD =
4337 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4338 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4339 ReductionsBufferTy, BufferVD, 0, En.index());
4340
4341 switch (RI.EvaluationKind) {
4342 case EvalKind::Scalar: {
4343 Type *ElemType = RI.ElementType;
4344
4345 if (!IsByRef.empty() && IsByRef[En.index()]) {
4346 ElemType = RI.ByRefElementType;
4347 if (RI.DataPtrPtrGen) {
4348 InsertPointOrErrorTy GenResult =
4349 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4350
4351 if (!GenResult)
4352 return GenResult.takeError();
4353
4354 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4355 }
4356 }
4357
4358 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4359 Builder.CreateStore(TargetElement, ElemPtr);
4360 break;
4361 }
4362 case EvalKind::Complex: {
4363 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4364 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4365 Value *SrcReal = Builder.CreateLoad(
4366 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4367 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4368 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4369 Value *SrcImg = Builder.CreateLoad(
4370 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4371
4372 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4373 RI.ElementType, ElemPtr, 0, 0, ".realp");
4374 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4375 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4376 Builder.CreateStore(SrcReal, DestRealPtr);
4377 Builder.CreateStore(SrcImg, DestImgPtr);
4378 break;
4379 }
4380 case EvalKind::Aggregate: {
4381 Value *SizeVal =
4382 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4383 Builder.CreateMemCpy(
4384 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4385 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4386 SizeVal, false);
4387 break;
4388 }
4389 }
4390 }
4391
4392 Builder.CreateRetVoid();
4393 return GtLCFunc;
4394}
4395
4396Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4397 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4398 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4399 IRBuilder<>::InsertPointGuard IPG(Builder);
4400 LLVMContext &Ctx = M.getContext();
4401 auto *FuncTy = FunctionType::get(
4402 Builder.getVoidTy(),
4403 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4404 /* IsVarArg */ false);
4405 Function *GtLRFunc =
4407 "_omp_reduction_global_to_list_reduce_func", &M);
4408 GtLRFunc->setAttributes(FuncAttrs);
4409 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4410 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4411 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4412
4413 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4414 Builder.SetInsertPoint(EntryBlock);
4415 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4416
4417 // Buffer: global reduction buffer.
4418 Argument *BufferArg = GtLRFunc->getArg(0);
4419 // Idx: index of the buffer.
4420 Argument *IdxArg = GtLRFunc->getArg(1);
4421 // ReduceList: thread local Reduce list.
4422 Argument *ReduceListArg = GtLRFunc->getArg(2);
4423
4424 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4425 BufferArg->getName() + ".addr");
4426 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4427 IdxArg->getName() + ".addr");
4428 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4429 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4430 ArrayType *RedListArrayTy =
4431 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4432
4433 // 1. Build a list of reduction variables.
4434 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4435 Value *LocalReduceList =
4436 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4437
4438 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4439
4440 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4441 BufferArgAlloca, Builder.getPtrTy(),
4442 BufferArgAlloca->getName() + ".ascast");
4443 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4444 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4445 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4446 ReduceListArgAlloca, Builder.getPtrTy(),
4447 ReduceListArgAlloca->getName() + ".ascast");
4448 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4449 LocalReduceList, Builder.getPtrTy(),
4450 LocalReduceList->getName() + ".ascast");
4451
4452 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4453 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4454 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4455
4456 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4457 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4458 Type *IndexTy = Builder.getIndexTy(
4459 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4460 for (auto En : enumerate(ReductionInfos)) {
4461 const ReductionInfo &RI = En.value();
4462
4463 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4464 RedListArrayTy, ReductionList,
4465 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4466 // Global = Buffer.VD[Idx];
4467 Value *BufferVD =
4468 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4469 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4470 ReductionsBufferTy, BufferVD, 0, En.index());
4471
4472 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4473 // Get source descriptor from the reduce list
4474 Value *ReduceListVal =
4475 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4476 Value *SrcElementPtrPtr =
4477 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4478 {ConstantInt::get(IndexTy, 0),
4479 ConstantInt::get(IndexTy, En.index())});
4480 Value *SrcDescriptorAddr =
4481 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4482
4483 // Copy descriptor from source and update base_ptr to global buffer data
4484 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4485 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4486 if (!ByRefAlloc)
4487 return ByRefAlloc.takeError();
4488
4489 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4490 } else {
4491 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4492 }
4493 }
4494
4495 // Call reduce_function(ReduceList, GlobalReduceList)
4496 Value *ReduceList =
4497 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4498 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4499 ->addFnAttr(Attribute::NoUnwind);
4500 Builder.CreateRetVoid();
4501 return GtLRFunc;
4502}
4503
4504std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4505 std::string Suffix =
4506 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4507 return (Name + Suffix).str();
4508}
4509
4510Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4511 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4513 AttributeList FuncAttrs) {
4514 IRBuilder<>::InsertPointGuard IPG(Builder);
4515 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4516 {Builder.getPtrTy(), Builder.getPtrTy()},
4517 /* IsVarArg */ false);
4518 std::string Name = getReductionFuncName(ReducerName);
4519 Function *ReductionFunc =
4521 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4522 ReductionFunc->setAttributes(FuncAttrs);
4523 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4524 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4525 BasicBlock *EntryBB =
4526 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4527 Builder.SetInsertPoint(EntryBB);
4528 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4529
4530 // Need to alloca memory here and deal with the pointers before getting
4531 // LHS/RHS pointers out
4532 Value *LHSArrayPtr = nullptr;
4533 Value *RHSArrayPtr = nullptr;
4534 Argument *Arg0 = ReductionFunc->getArg(0);
4535 Argument *Arg1 = ReductionFunc->getArg(1);
4536 Type *Arg0Type = Arg0->getType();
4537 Type *Arg1Type = Arg1->getType();
4538
4539 Value *LHSAlloca =
4540 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4541 Value *RHSAlloca =
4542 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4543 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4544 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4545 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4546 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4547 Builder.CreateStore(Arg0, LHSAddrCast);
4548 Builder.CreateStore(Arg1, RHSAddrCast);
4549 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4550 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4551
4552 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4553 Type *IndexTy = Builder.getIndexTy(
4554 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4555 SmallVector<Value *> LHSPtrs, RHSPtrs;
4556 for (auto En : enumerate(ReductionInfos)) {
4557 const ReductionInfo &RI = En.value();
4558 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4559 RedArrayTy, RHSArrayPtr,
4560 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4561 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4562 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4563 RHSI8Ptr, RI.PrivateVariable->getType(),
4564 RHSI8Ptr->getName() + ".ascast");
4565
4566 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4567 RedArrayTy, LHSArrayPtr,
4568 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4569 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4570 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4571 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4572
4574 LHSPtrs.emplace_back(LHSPtr);
4575 RHSPtrs.emplace_back(RHSPtr);
4576 } else {
4577 Value *LHS = LHSPtr;
4578 Value *RHS = RHSPtr;
4579
4580 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4581 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4582 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4583 }
4584
4585 Value *Reduced;
4586 InsertPointOrErrorTy AfterIP =
4587 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4588 if (!AfterIP)
4589 return AfterIP.takeError();
4590 if (!Builder.GetInsertBlock())
4591 return ReductionFunc;
4592
4593 Builder.restoreIP(*AfterIP);
4594
4595 if (!IsByRef.empty() && !IsByRef[En.index()])
4596 Builder.CreateStore(Reduced, LHSPtr);
4597 }
4598 }
4599
4601 for (auto En : enumerate(ReductionInfos)) {
4602 unsigned Index = En.index();
4603 const ReductionInfo &RI = En.value();
4604 Value *LHSFixupPtr, *RHSFixupPtr;
4605 Builder.restoreIP(RI.ReductionGenClang(
4606 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4607
4608 // Fix the CallBack code genereated to use the correct Values for the LHS
4609 // and RHS
4610 LHSFixupPtr->replaceUsesWithIf(
4611 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4612 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4613 ReductionFunc;
4614 });
4615 RHSFixupPtr->replaceUsesWithIf(
4616 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4617 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4618 ReductionFunc;
4619 });
4620 }
4621
4622 Builder.CreateRetVoid();
4623 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4624 // to the entry block (this is dones for higher opt levels by later passes in
4625 // the pipeline). This has caused issues because non-entry `alloca`s force the
4626 // function to use dynamic stack allocations and we might run out of scratch
4627 // memory.
4628 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4629
4630 return ReductionFunc;
4631}
4632
4633static void
4635 bool IsGPU) {
4636 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4637 (void)RI;
4638 assert(RI.Variable && "expected non-null variable");
4639 assert(RI.PrivateVariable && "expected non-null private variable");
4640 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4641 "expected non-null reduction generator callback");
4642 if (!IsGPU) {
4643 assert(
4644 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4645 "expected variables and their private equivalents to have the same "
4646 "type");
4647 }
4648 assert(RI.Variable->getType()->isPointerTy() &&
4649 "expected variables to be pointers");
4650 }
4651}
4652
4654 const LocationDescription &Loc, InsertPointTy AllocaIP,
4655 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4656 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4657 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4658 Value *SrcLocInfo) {
4659 if (!updateToLocation(Loc))
4660 return InsertPointTy();
4661 Builder.restoreIP(CodeGenIP);
4662 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4663 LLVMContext &Ctx = M.getContext();
4664
4665 // Source location for the ident struct
4666 if (!SrcLocInfo) {
4667 uint32_t SrcLocStrSize;
4668 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4669 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4670 }
4671
4672 if (ReductionInfos.size() == 0)
4673 return Builder.saveIP();
4674
4675 BasicBlock *ContinuationBlock = nullptr;
4677 // Copied code from createReductions
4678 BasicBlock *InsertBlock = Loc.IP.getBlock();
4679 ContinuationBlock =
4680 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
4681 InsertBlock->getTerminator()->eraseFromParent();
4682 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
4683 }
4684
4685 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4686 AttributeList FuncAttrs;
4687 AttrBuilder AttrBldr(Ctx);
4688 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4689 AttrBldr.addAttribute(Attr);
4690 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4691 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4692
4693 CodeGenIP = Builder.saveIP();
4694 Expected<Function *> ReductionResult = createReductionFunction(
4695 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4696 ReductionGenCBKind, FuncAttrs);
4697 if (!ReductionResult)
4698 return ReductionResult.takeError();
4699 Function *ReductionFunc = *ReductionResult;
4700 Builder.restoreIP(CodeGenIP);
4701
4702 // Set the grid value in the config needed for lowering later on
4703 if (GridValue.has_value())
4704 Config.setGridValue(GridValue.value());
4705 else
4706 Config.setGridValue(getGridValue(T, ReductionFunc));
4707
4708 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4709 // RedList, shuffle_reduce_func, interwarp_copy_func);
4710 // or
4711 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4712 Value *Res;
4713
4714 // 1. Build a list of reduction variables.
4715 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4716 auto Size = ReductionInfos.size();
4717 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4718 Type *FuncPtrTy =
4719 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4720 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4721 CodeGenIP = Builder.saveIP();
4722 Builder.restoreIP(AllocaIP);
4723 Value *ReductionListAlloca =
4724 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4725 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4726 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4727 Builder.restoreIP(CodeGenIP);
4728 Type *IndexTy = Builder.getIndexTy(
4729 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4730 for (auto En : enumerate(ReductionInfos)) {
4731 const ReductionInfo &RI = En.value();
4732 Value *ElemPtr = Builder.CreateInBoundsGEP(
4733 RedArrayTy, ReductionList,
4734 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4735
4736 Value *PrivateVar = RI.PrivateVariable;
4737 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4738 if (IsByRefElem)
4739 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4740
4741 Value *CastElem =
4742 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4743 Builder.CreateStore(CastElem, ElemPtr);
4744 }
4745 CodeGenIP = Builder.saveIP();
4746 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4747 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4748
4749 if (!SarFunc)
4750 return SarFunc.takeError();
4751
4752 Expected<Function *> CopyResult =
4753 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4754 if (!CopyResult)
4755 return CopyResult.takeError();
4756 Function *WcFunc = *CopyResult;
4757 Builder.restoreIP(CodeGenIP);
4758
4759 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4760
4761 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4762 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4763 // not currently use it. It is computed here conservatively as max(element
4764 // sizes) * N rather than the exact sum, which over-calculates the size for
4765 // mixed reduction types but is harmless given the argument is unused.
4766 // TODO: Consider dropping this computation if the runtime API is ever revised
4767 // to remove the unused parameter.
4768 unsigned MaxDataSize = 0;
4769 SmallVector<Type *> ReductionTypeArgs;
4770 for (auto En : enumerate(ReductionInfos)) {
4771 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4772 // the actual data size stored in the global reduction buffer, consistent
4773 // with the ReductionsBufferTy struct used for GEP offsets below.
4774 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4775 ? En.value().ByRefElementType
4776 : En.value().ElementType;
4777 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4778 if (Size > MaxDataSize)
4779 MaxDataSize = Size;
4780 ReductionTypeArgs.emplace_back(RedTypeArg);
4781 }
4782 Value *ReductionDataSize =
4783 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4784
4785 // Helper function to copy thread-local data back to the original reduction
4786 // list.
4787 Function *CopyScratchToListFunc = nullptr;
4788 // Thread-local storage for the reduction variables.
4789 Value *ScratchForCopyBack = nullptr;
4790 // RL pointer to which the final value from the per-thread scratch should be
4791 // copied back. (Basically RL, appropriately casted if necessary.)
4792 Value *RLForCopyBack = RL;
4793
4794 if (!IsTeamsReduction) {
4795 Value *SarFuncCast =
4796 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4797 Value *WcFuncCast =
4798 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4799 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4800 WcFuncCast};
4802 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4803 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4804 } else {
4805 CodeGenIP = Builder.saveIP();
4806 StructType *ReductionsBufferTy = StructType::create(
4807 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4808
4809 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4810 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4811 if (!LtGCFunc)
4812 return LtGCFunc.takeError();
4813
4814 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
4815 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4816 if (!GtLCFunc)
4817 return GtLCFunc.takeError();
4818
4819 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
4820 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
4821 if (!GtLRFunc)
4822 return GtLRFunc.takeError();
4823
4824 Builder.restoreIP(CodeGenIP);
4825
4826 // The runtime's cross-team final aggregate uses the storage pointed at by
4827 // its reduce-list argument as per-thread scratch. When the surrounding
4828 // kernel is already in SPMD execution mode, clang emitted each reduction
4829 // private as a per-thread `alloca addrspace(5)`, so the original red_list
4830 // (RL) is already per-thread and nothing else is needed.
4831 //
4832 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
4833 // Generic-mode globalization put the reduction private into team-shared
4834 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
4835 // point all threads of the last team would race on the shared LDS slot.
4836 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
4837 // value in, and hand the per-thread RL to the runtime instead. The writer
4838 // thread copies the final value from that per-thread scratch back to RL
4839 // before running the existing combine path below.
4840
4841 // Thread-local RL (might need localization below before being passed to the
4842 // runtime).
4843 Value *RuntimeRL = RL;
4844
4845 if (!IsSPMD) {
4846 CodeGenIP = Builder.saveIP();
4847 Builder.restoreIP(AllocaIP);
4848 // Allocate thread-local buffer for the reduction variables.
4849 Value *PerThreadScratchAlloca = Builder.CreateAlloca(
4850 ReductionsBufferTy, /*ArraySize=*/nullptr, ".omp.reduction.scratch");
4851 Value *PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
4852 PerThreadScratchAlloca, PtrTy,
4853 PerThreadScratchAlloca->getName() + ".ascast");
4854 // Allocate thread-local buffer for the pointers to the reduction
4855 // variables.
4856 Value *PerThreadRedListAlloca =
4857 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
4858 ".omp.reduction.per_thread_red_list");
4859 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
4860 PerThreadRedListAlloca, PtrTy,
4861 PerThreadRedListAlloca->getName() + ".ascast");
4862 Builder.restoreIP(CodeGenIP);
4863
4864 // Iterate over the reduction variables and copy the team-local value to
4865 // the thread-local buffer.
4866 for (auto En : enumerate(ReductionInfos)) {
4867 const ReductionInfo &RI = En.value();
4868 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4869
4870 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
4871 ReductionsBufferTy, PerThreadScratch, 0, En.index());
4872 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
4873 0, En.index());
4874
4875 Value *RuntimeListEntry = FieldPtr;
4876 if (IsByRefElem && RI.DataPtrPtrGen) {
4877 Value *SrcDescriptor =
4878 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
4879 Expected<Value *> Descriptor = createReductionDescriptorCopy(
4880 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
4881 if (!Descriptor)
4882 return Descriptor.takeError();
4883 RuntimeListEntry = *Descriptor;
4884 }
4885 Builder.CreateStore(RuntimeListEntry, Slot);
4886 }
4887 // The copy helpers were emitted with default-AS (AS 0) pointer params
4888 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
4889 // but PerThreadScratch and RL live in the target's default AS, which
4890 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
4891 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
4892 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
4893 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
4894 PerThreadScratch, CopyArg0Ty);
4895 RLForCopyBack =
4896 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
4897 // Use index 0 because there is no array of target values to index into,
4898 // there is only one thread-local memory slot.
4899 // restoreIP above left a stale/empty debug location; this inlinable call
4900 // to a debug-info-bearing helper needs one or the verifier rejects the
4901 // module ("!dbg attachment points at wrong subprogram") after inlining.
4902 Builder.SetCurrentDebugLocation(Loc.DL);
4903 Builder.CreateCall(
4904 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
4905 CopyScratchToListFunc = *GtLCFunc;
4906 }
4907
4908 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
4909 *LtGCFunc, *GtLCFunc, *GtLRFunc};
4910
4911 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
4912 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
4913 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
4914 }
4915
4916 // 5. Build if (res == 1)
4917 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
4918 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
4919 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
4920 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
4921
4922 // 6. Build then branch: where we have reduced values in the master
4923 // thread in each team.
4924 // __kmpc_end_reduce{_nowait}(<gtid>);
4925 // break;
4926 emitBlock(ThenBB, CurFunc);
4927
4928 // Copy the writer thread's per-thread scratch result back into the original
4929 // red-list storage before the existing combine path reads RI.PrivateVariable.
4930 // Set a debug location: this inlinable call to a debug-info-bearing helper
4931 // needs one or the verifier rejects the module after inlining.
4932 if (ScratchForCopyBack) {
4933 Builder.SetCurrentDebugLocation(Loc.DL);
4934 Builder.CreateCall(
4935 CopyScratchToListFunc,
4936 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
4937 }
4938
4939 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
4940 for (auto En : enumerate(ReductionInfos)) {
4941 const ReductionInfo &RI = En.value();
4943 Value *RedValue = RI.Variable;
4944
4945 Value *RHS =
4946 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
4947
4949 Value *LHSPtr, *RHSPtr;
4950 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
4951 &LHSPtr, &RHSPtr, CurFunc));
4952
4953 // Fix the CallBack code genereated to use the correct Values for the LHS
4954 // and RHS. Cast to match types before replacing (necessary to handle
4955 // different address spaces).
4956 if (LHSPtr->getType() != RedValue->getType())
4957 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
4958 RedValue, LHSPtr->getType());
4959 if (RHSPtr->getType() != RHS->getType())
4960 RHS =
4961 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
4962
4963 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
4964 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4965 ReductionFunc;
4966 });
4967 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
4968 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4969 ReductionFunc;
4970 });
4971 } else {
4972 if (IsByRef.empty() || !IsByRef[En.index()]) {
4973 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
4974 "red.value." + Twine(En.index()));
4975 }
4976 Value *PrivateRedValue = Builder.CreateLoad(
4977 ValueType, RHS, "red.private.value" + Twine(En.index()));
4978 Value *Reduced;
4979 InsertPointOrErrorTy AfterIP =
4980 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
4981 if (!AfterIP)
4982 return AfterIP.takeError();
4983 Builder.restoreIP(*AfterIP);
4984
4985 if (!IsByRef.empty() && !IsByRef[En.index()])
4986 Builder.CreateStore(Reduced, RI.Variable);
4987 }
4988 }
4989 emitBlock(ExitBB, CurFunc);
4990 if (ContinuationBlock) {
4991 Builder.CreateBr(ContinuationBlock);
4992 Builder.SetInsertPoint(ContinuationBlock);
4993 }
4994 Config.setEmitLLVMUsed();
4995
4996 return Builder.saveIP();
4997}
4998
5000 Type *VoidTy = Type::getVoidTy(M.getContext());
5001 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5002 auto *FuncTy =
5003 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5005 ".omp.reduction.func", &M);
5006}
5007
5009 Function *ReductionFunc,
5011 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5012 IRBuilder<>::InsertPointGuard IPG(Builder);
5013 Module *Module = ReductionFunc->getParent();
5014 BasicBlock *ReductionFuncBlock =
5015 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5016 Builder.SetInsertPoint(ReductionFuncBlock);
5017 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5018 Value *LHSArrayPtr = nullptr;
5019 Value *RHSArrayPtr = nullptr;
5020 if (IsGPU) {
5021 // Need to alloca memory here and deal with the pointers before getting
5022 // LHS/RHS pointers out
5023 //
5024 Argument *Arg0 = ReductionFunc->getArg(0);
5025 Argument *Arg1 = ReductionFunc->getArg(1);
5026 Type *Arg0Type = Arg0->getType();
5027 Type *Arg1Type = Arg1->getType();
5028
5029 Value *LHSAlloca =
5030 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5031 Value *RHSAlloca =
5032 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5033 Value *LHSAddrCast =
5034 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5035 Value *RHSAddrCast =
5036 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5037 Builder.CreateStore(Arg0, LHSAddrCast);
5038 Builder.CreateStore(Arg1, RHSAddrCast);
5039 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5040 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5041 } else {
5042 LHSArrayPtr = ReductionFunc->getArg(0);
5043 RHSArrayPtr = ReductionFunc->getArg(1);
5044 }
5045
5046 unsigned NumReductions = ReductionInfos.size();
5047 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5048
5049 for (auto En : enumerate(ReductionInfos)) {
5050 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5051 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5052 RedArrayTy, LHSArrayPtr, 0, En.index());
5053 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5054 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5055 LHSI8Ptr, RI.Variable->getType());
5056 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5057 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5058 RedArrayTy, RHSArrayPtr, 0, En.index());
5059 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5060 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5061 RHSI8Ptr, RI.PrivateVariable->getType());
5062 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5063 Value *Reduced;
5065 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5066 if (!AfterIP)
5067 return AfterIP.takeError();
5068
5069 Builder.restoreIP(*AfterIP);
5070 // TODO: Consider flagging an error.
5071 if (!Builder.GetInsertBlock())
5072 return Error::success();
5073
5074 // store is inside of the reduction region when using by-ref
5075 if (!IsByRef[En.index()])
5076 Builder.CreateStore(Reduced, LHSPtr);
5077 }
5078 Builder.CreateRetVoid();
5079 return Error::success();
5080}
5081
5083 const LocationDescription &Loc, InsertPointTy AllocaIP,
5084 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5085 bool IsNoWait, bool IsTeamsReduction) {
5086 assert(ReductionInfos.size() == IsByRef.size());
5087 if (Config.isGPU())
5088 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5089 IsByRef, IsNoWait, IsTeamsReduction);
5090
5091 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5092
5093 if (!updateToLocation(Loc))
5094 return InsertPointTy();
5095
5096 if (ReductionInfos.size() == 0)
5097 return Builder.saveIP();
5098
5099 BasicBlock *InsertBlock = Loc.IP.getBlock();
5100 BasicBlock *ContinuationBlock =
5101 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
5102 InsertBlock->getTerminator()->eraseFromParent();
5103
5104 // Create and populate array of type-erased pointers to private reduction
5105 // values.
5106 unsigned NumReductions = ReductionInfos.size();
5107 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5108 Builder.SetInsertPoint(AllocaIP.getBlock()->getTerminator());
5109 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5110
5111 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
5112
5113 for (auto En : enumerate(ReductionInfos)) {
5114 unsigned Index = En.index();
5115 const ReductionInfo &RI = En.value();
5116 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5117 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5118 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5119 }
5120
5121 // Emit a call to the runtime function that orchestrates the reduction.
5122 // Declare the reduction function in the process.
5123 Type *IndexTy = Builder.getIndexTy(
5124 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5125 Function *Func = Builder.GetInsertBlock()->getParent();
5126 Module *Module = Func->getParent();
5127 uint32_t SrcLocStrSize;
5128 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5129 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5130 return RI.AtomicReductionGen;
5131 });
5132 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5133 CanGenerateAtomic
5134 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5135 : IdentFlag(0));
5136 Value *ThreadId = getOrCreateThreadID(Ident);
5137 Constant *NumVariables = Builder.getInt32(NumReductions);
5138 const DataLayout &DL = Module->getDataLayout();
5139 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5140 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5141 Function *ReductionFunc = getFreshReductionFunc(*Module);
5142 Value *Lock = getOMPCriticalRegionLock(".reduction");
5144 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5145 : RuntimeFunction::OMPRTL___kmpc_reduce);
5146 CallInst *ReduceCall =
5147 createRuntimeFunctionCall(ReduceFunc,
5148 {Ident, ThreadId, NumVariables, RedArraySize,
5149 RedArray, ReductionFunc, Lock},
5150 "reduce");
5151
5152 // Create final reduction entry blocks for the atomic and non-atomic case.
5153 // Emit IR that dispatches control flow to one of the blocks based on the
5154 // reduction supporting the atomic mode.
5155 BasicBlock *NonAtomicRedBlock =
5156 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5157 BasicBlock *AtomicRedBlock =
5158 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5159 SwitchInst *Switch =
5160 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5161 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5162 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5163
5164 // Populate the non-atomic reduction using the elementwise reduction function.
5165 // This loads the elements from the global and private variables and reduces
5166 // them before storing back the result to the global variable.
5167 Builder.SetInsertPoint(NonAtomicRedBlock);
5168 for (auto En : enumerate(ReductionInfos)) {
5169 const ReductionInfo &RI = En.value();
5171 // We have one less load for by-ref case because that load is now inside of
5172 // the reduction region
5173 Value *RedValue = RI.Variable;
5174 if (!IsByRef[En.index()]) {
5175 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5176 "red.value." + Twine(En.index()));
5177 }
5178 Value *PrivateRedValue =
5179 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5180 "red.private.value." + Twine(En.index()));
5181 Value *Reduced;
5182 InsertPointOrErrorTy AfterIP =
5183 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5184 if (!AfterIP)
5185 return AfterIP.takeError();
5186 Builder.restoreIP(*AfterIP);
5187
5188 if (!Builder.GetInsertBlock())
5189 return InsertPointTy();
5190 // for by-ref case, the load is inside of the reduction region
5191 if (!IsByRef[En.index()])
5192 Builder.CreateStore(Reduced, RI.Variable);
5193 }
5194 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5195 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5196 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5197 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5198 Builder.CreateBr(ContinuationBlock);
5199
5200 // Populate the atomic reduction using the atomic elementwise reduction
5201 // function. There are no loads/stores here because they will be happening
5202 // inside the atomic elementwise reduction.
5203 Builder.SetInsertPoint(AtomicRedBlock);
5204 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5205 for (const ReductionInfo &RI : ReductionInfos) {
5207 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5208 if (!AfterIP)
5209 return AfterIP.takeError();
5210 Builder.restoreIP(*AfterIP);
5211 if (!Builder.GetInsertBlock())
5212 return InsertPointTy();
5213 }
5214 Builder.CreateBr(ContinuationBlock);
5215 } else {
5216 Builder.CreateUnreachable();
5217 }
5218
5219 // Populate the outlined reduction function using the elementwise reduction
5220 // function. Partial values are extracted from the type-erased array of
5221 // pointers to private variables.
5222 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5223 IsByRef, /*isGPU=*/false);
5224 if (Err)
5225 return Err;
5226
5227 if (!Builder.GetInsertBlock())
5228 return InsertPointTy();
5229
5230 Builder.SetInsertPoint(ContinuationBlock);
5231 return Builder.saveIP();
5232}
5233
5236 BodyGenCallbackTy BodyGenCB,
5237 FinalizeCallbackTy FiniCB) {
5238 if (!updateToLocation(Loc))
5239 return Loc.IP;
5240
5241 Directive OMPD = Directive::OMPD_master;
5242 uint32_t SrcLocStrSize;
5243 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5244 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5245 Value *ThreadId = getOrCreateThreadID(Ident);
5246 Value *Args[] = {Ident, ThreadId};
5247
5248 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5249 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5250
5251 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5252 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5253
5254 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5255 /*Conditional*/ true, /*hasFinalize*/ true);
5256}
5257
5260 BodyGenCallbackTy BodyGenCB,
5261 FinalizeCallbackTy FiniCB, Value *Filter) {
5262 if (!updateToLocation(Loc))
5263 return Loc.IP;
5264
5265 Directive OMPD = Directive::OMPD_masked;
5266 uint32_t SrcLocStrSize;
5267 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5268 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5269 Value *ThreadId = getOrCreateThreadID(Ident);
5270 Value *Args[] = {Ident, ThreadId, Filter};
5271 Value *ArgsEnd[] = {Ident, ThreadId};
5272
5273 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5274 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5275
5276 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5277 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5278
5279 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5280 /*Conditional*/ true, /*hasFinalize*/ true);
5281}
5282
5284 llvm::FunctionCallee Callee,
5286 const llvm::Twine &Name) {
5287 llvm::CallInst *Call = Builder.CreateCall(
5288 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5289 Call->setDoesNotThrow();
5290 return Call;
5291}
5292
5293// Expects input basic block is dominated by BeforeScanBB.
5294// Once Scan directive is encountered, the code after scan directive should be
5295// dominated by AfterScanBB. Scan directive splits the code sequence to
5296// scan and input phase. Based on whether inclusive or exclusive
5297// clause is used in the scan directive and whether input loop or scan loop
5298// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5299// input loop and second is the scan loop. The code generated handles only
5300// inclusive scans now.
5302 const LocationDescription &Loc, InsertPointTy AllocaIP,
5303 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5304 bool IsInclusive, ScanInfo *ScanRedInfo) {
5305 if (ScanRedInfo->OMPFirstScanLoop) {
5306 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5307 ScanVarsType, ScanRedInfo);
5308 if (Err)
5309 return Err;
5310 }
5311 if (!updateToLocation(Loc))
5312 return Loc.IP;
5313
5314 llvm::Value *IV = ScanRedInfo->IV;
5315
5316 if (ScanRedInfo->OMPFirstScanLoop) {
5317 // Emit buffer[i] = red; at the end of the input phase.
5318 for (size_t i = 0; i < ScanVars.size(); i++) {
5319 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5320 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5321 Type *DestTy = ScanVarsType[i];
5322 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5323 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5324
5325 Builder.CreateStore(Src, Val);
5326 }
5327 }
5328 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5329 emitBlock(ScanRedInfo->OMPScanDispatch,
5330 Builder.GetInsertBlock()->getParent());
5331
5332 if (!ScanRedInfo->OMPFirstScanLoop) {
5333 IV = ScanRedInfo->IV;
5334 // Emit red = buffer[i]; at the entrance to the scan phase.
5335 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5336 for (size_t i = 0; i < ScanVars.size(); i++) {
5337 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5338 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5339 Type *DestTy = ScanVarsType[i];
5340 Value *SrcPtr =
5341 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5342 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5343 Builder.CreateStore(Src, ScanVars[i]);
5344 }
5345 }
5346
5347 // TODO: Update it to CreateBr and remove dead blocks
5348 llvm::Value *CmpI = Builder.getInt1(true);
5349 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5350 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5351 ScanRedInfo->OMPAfterScanBlock);
5352 } else {
5353 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5354 ScanRedInfo->OMPBeforeScanBlock);
5355 }
5356 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5357 Builder.GetInsertBlock()->getParent());
5358 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5359 return Builder.saveIP();
5360}
5361
5362Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5363 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5364 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5365
5366 Builder.restoreIP(AllocaIP);
5367 // Create the shared pointer at alloca IP.
5368 for (size_t i = 0; i < ScanVars.size(); i++) {
5369 llvm::Value *BuffPtr =
5370 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5371 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5372 }
5373
5374 // Allocate temporary buffer by master thread
5375 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5376 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5377 Builder.restoreIP(CodeGenIP);
5378 Value *AllocSpan =
5379 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5380 for (size_t i = 0; i < ScanVars.size(); i++) {
5381 Type *IntPtrTy = Builder.getInt32Ty();
5382 Constant *Allocsize = ConstantExpr::getSizeOf(ScanVarsType[i]);
5383 Allocsize = ConstantExpr::getTruncOrBitCast(Allocsize, IntPtrTy);
5384 Value *Buff = Builder.CreateMalloc(IntPtrTy, ScanVarsType[i], Allocsize,
5385 AllocSpan, nullptr, "arr");
5386 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5387 }
5388 return Error::success();
5389 };
5390 // TODO: Perform finalization actions for variables. This has to be
5391 // called for variables which have destructors/finalizers.
5392 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5393
5394 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5395 llvm::Value *FilterVal = Builder.getInt32(0);
5397 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5398
5399 if (!AfterIP)
5400 return AfterIP.takeError();
5401 Builder.restoreIP(*AfterIP);
5402 BasicBlock *InputBB = Builder.GetInsertBlock();
5403 if (InputBB->hasTerminator())
5404 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
5405 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5406 if (!AfterIP)
5407 return AfterIP.takeError();
5408 Builder.restoreIP(*AfterIP);
5409
5410 return Error::success();
5411}
5412
5413Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5414 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5415 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5416 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5417 Builder.restoreIP(CodeGenIP);
5418 for (ReductionInfo RedInfo : ReductionInfos) {
5419 Value *PrivateVar = RedInfo.PrivateVariable;
5420 Value *OrigVar = RedInfo.Variable;
5421 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5422 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5423
5424 Type *SrcTy = RedInfo.ElementType;
5425 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5426 "arrayOffset");
5427 Value *Src = Builder.CreateLoad(SrcTy, Val);
5428
5429 Builder.CreateStore(Src, OrigVar);
5430 Builder.CreateFree(Buff);
5431 }
5432 return Error::success();
5433 };
5434 // TODO: Perform finalization actions for variables. This has to be
5435 // called for variables which have destructors/finalizers.
5436 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5437
5438 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5439 Builder.SetInsertPoint(TI);
5440 else
5441 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5442
5443 llvm::Value *FilterVal = Builder.getInt32(0);
5445 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5446
5447 if (!AfterIP)
5448 return AfterIP.takeError();
5449 Builder.restoreIP(*AfterIP);
5450 BasicBlock *InputBB = Builder.GetInsertBlock();
5451 if (InputBB->hasTerminator())
5452 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
5453 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5454 if (!AfterIP)
5455 return AfterIP.takeError();
5456 Builder.restoreIP(*AfterIP);
5457 return Error::success();
5458}
5459
5461 const LocationDescription &Loc,
5463 ScanInfo *ScanRedInfo) {
5464
5465 if (!updateToLocation(Loc))
5466 return Loc.IP;
5467 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5468 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5469 Builder.restoreIP(CodeGenIP);
5470 Function *CurFn = Builder.GetInsertBlock()->getParent();
5471 // for (int k = 0; k <= ceil(log2(n)); ++k)
5472 llvm::BasicBlock *LoopBB =
5473 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5474 llvm::BasicBlock *ExitBB =
5475 splitBB(Builder, false, "omp.outer.log.scan.exit");
5477 Builder.GetInsertBlock()->getModule(),
5478 (llvm::Intrinsic::ID)llvm::Intrinsic::log2, Builder.getDoubleTy());
5479 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5480 llvm::Value *Arg =
5481 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5482 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5484 Builder.GetInsertBlock()->getModule(),
5485 (llvm::Intrinsic::ID)llvm::Intrinsic::ceil, Builder.getDoubleTy());
5486 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5487 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5488 llvm::Value *NMin1 = Builder.CreateNUWSub(
5489 ScanRedInfo->Span,
5490 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5491 Builder.SetInsertPoint(InputBB);
5492 Builder.CreateBr(LoopBB);
5493 emitBlock(LoopBB, CurFn);
5494 Builder.SetInsertPoint(LoopBB);
5495
5496 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5497 // size pow2k = 1;
5498 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5499 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5500 InputBB);
5501 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5502 InputBB);
5503 // for (size i = n - 1; i >= 2 ^ k; --i)
5504 // tmp[i] op= tmp[i-pow2k];
5505 llvm::BasicBlock *InnerLoopBB =
5506 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5507 llvm::BasicBlock *InnerExitBB =
5508 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5509 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5510 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5511 emitBlock(InnerLoopBB, CurFn);
5512 Builder.SetInsertPoint(InnerLoopBB);
5513 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5514 IVal->addIncoming(NMin1, LoopBB);
5515 for (ReductionInfo RedInfo : ReductionInfos) {
5516 Value *ReductionVal = RedInfo.PrivateVariable;
5517 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5518 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5519 Type *DestTy = RedInfo.ElementType;
5520 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5521 Value *LHSPtr =
5522 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5523 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5524 Value *RHSPtr =
5525 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5526 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5527 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5528 llvm::Value *Result;
5529 InsertPointOrErrorTy AfterIP =
5530 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5531 if (!AfterIP)
5532 return AfterIP.takeError();
5533 Builder.CreateStore(Result, LHSPtr);
5534 }
5535 llvm::Value *NextIVal = Builder.CreateNUWSub(
5536 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5537 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5538 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5539 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5540 emitBlock(InnerExitBB, CurFn);
5541 llvm::Value *Next = Builder.CreateNUWAdd(
5542 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5543 Counter->addIncoming(Next, Builder.GetInsertBlock());
5544 // pow2k <<= 1;
5545 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5546 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5547 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5548 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5549 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5550 return Error::success();
5551 };
5552
5553 // TODO: Perform finalization actions for variables. This has to be
5554 // called for variables which have destructors/finalizers.
5555 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5556
5557 llvm::Value *FilterVal = Builder.getInt32(0);
5559 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5560
5561 if (!AfterIP)
5562 return AfterIP.takeError();
5563 Builder.restoreIP(*AfterIP);
5564 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5565
5566 if (!AfterIP)
5567 return AfterIP.takeError();
5568 Builder.restoreIP(*AfterIP);
5569 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5570 if (Err)
5571 return Err;
5572
5573 return AfterIP;
5574}
5575
5576Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5577 llvm::function_ref<Error()> InputLoopGen,
5578 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5579 ScanInfo *ScanRedInfo) {
5580
5581 {
5582 // Emit loop with input phase:
5583 // for (i: 0..<num_iters>) {
5584 // <input phase>;
5585 // buffer[i] = red;
5586 // }
5587 ScanRedInfo->OMPFirstScanLoop = true;
5588 Error Err = InputLoopGen();
5589 if (Err)
5590 return Err;
5591 }
5592 {
5593 // Emit loop with scan phase:
5594 // for (i: 0..<num_iters>) {
5595 // red = buffer[i];
5596 // <scan phase>;
5597 // }
5598 ScanRedInfo->OMPFirstScanLoop = false;
5599 Error Err = ScanLoopGen(Builder.saveIP());
5600 if (Err)
5601 return Err;
5602 }
5603 return Error::success();
5604}
5605
5606void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5607 Function *Fun = Builder.GetInsertBlock()->getParent();
5608 ScanRedInfo->OMPScanDispatch =
5609 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5610 ScanRedInfo->OMPAfterScanBlock =
5611 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5612 ScanRedInfo->OMPBeforeScanBlock =
5613 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5614 ScanRedInfo->OMPScanLoopExit =
5615 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5616}
5618 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5619 BasicBlock *PostInsertBefore, const Twine &Name) {
5620 Module *M = F->getParent();
5621 LLVMContext &Ctx = M->getContext();
5622 Type *IndVarTy = TripCount->getType();
5623
5624 // Create the basic block structure.
5625 BasicBlock *Preheader =
5626 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5627 BasicBlock *Header =
5628 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5629 BasicBlock *Cond =
5630 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5631 BasicBlock *Body =
5632 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5633 BasicBlock *Latch =
5634 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5635 BasicBlock *Exit =
5636 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5637 BasicBlock *After =
5638 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5639
5640 // Use specified DebugLoc for new instructions.
5641 Builder.SetCurrentDebugLocation(DL);
5642
5643 Builder.SetInsertPoint(Preheader);
5644 Builder.CreateBr(Header);
5645
5646 Builder.SetInsertPoint(Header);
5647 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5648 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5649 Builder.CreateBr(Cond);
5650
5651 Builder.SetInsertPoint(Cond);
5652 Value *Cmp =
5653 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5654 Builder.CreateCondBr(Cmp, Body, Exit);
5655
5656 Builder.SetInsertPoint(Body);
5657 Builder.CreateBr(Latch);
5658
5659 Builder.SetInsertPoint(Latch);
5660 Value *Next = Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5661 "omp_" + Name + ".next", /*HasNUW=*/true);
5662 Builder.CreateBr(Header);
5663 IndVarPHI->addIncoming(Next, Latch);
5664
5665 Builder.SetInsertPoint(Exit);
5666 Builder.CreateBr(After);
5667
5668 // Remember and return the canonical control flow.
5669 LoopInfos.emplace_front();
5670 CanonicalLoopInfo *CL = &LoopInfos.front();
5671
5672 CL->Header = Header;
5673 CL->Cond = Cond;
5674 CL->Latch = Latch;
5675 CL->Exit = Exit;
5676
5677#ifndef NDEBUG
5678 CL->assertOK();
5679#endif
5680 return CL;
5681}
5682
5685 LoopBodyGenCallbackTy BodyGenCB,
5686 Value *TripCount, const Twine &Name) {
5687 BasicBlock *BB = Loc.IP.getBlock();
5688 BasicBlock *NextBB = BB->getNextNode();
5689
5690 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5691 NextBB, NextBB, Name);
5692 BasicBlock *After = CL->getAfter();
5693
5694 // If location is not set, don't connect the loop.
5695 if (updateToLocation(Loc)) {
5696 // Split the loop at the insertion point: Branch to the preheader and move
5697 // every following instruction to after the loop (the After BB). Also, the
5698 // new successor is the loop's after block.
5699 spliceBB(Builder, After, /*CreateBranch=*/false);
5700 Builder.CreateBr(CL->getPreheader());
5701 }
5702
5703 // Emit the body content. We do it after connecting the loop to the CFG to
5704 // avoid that the callback encounters degenerate BBs.
5705 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5706 return Err;
5707
5708#ifndef NDEBUG
5709 CL->assertOK();
5710#endif
5711 return CL;
5712}
5713
5715 ScanInfos.emplace_front();
5716 ScanInfo *Result = &ScanInfos.front();
5717 return Result;
5718}
5719
5723 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5724 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5725 LocationDescription ComputeLoc =
5726 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5727 updateToLocation(ComputeLoc);
5728
5730
5732 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5733 ScanRedInfo->Span = TripCount;
5734 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5735 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5736
5737 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5738 Builder.restoreIP(CodeGenIP);
5739 ScanRedInfo->IV = IV;
5740 createScanBBs(ScanRedInfo);
5741 BasicBlock *InputBlock = Builder.GetInsertBlock();
5742 Instruction *Terminator = InputBlock->getTerminator();
5743 assert(Terminator->getNumSuccessors() == 1);
5744 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5745 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5746 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5747 Builder.GetInsertBlock()->getParent());
5748 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5749 emitBlock(ScanRedInfo->OMPScanLoopExit,
5750 Builder.GetInsertBlock()->getParent());
5751 Builder.CreateBr(ContinueBlock);
5752 Builder.SetInsertPoint(
5753 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5754 return BodyGenCB(Builder.saveIP(), IV);
5755 };
5756
5757 const auto &&InputLoopGen = [&]() -> Error {
5759 Builder.saveIP(), BodyGen, Start, Stop, Step, IsSigned, InclusiveStop,
5760 ComputeIP, Name, true, ScanRedInfo);
5761 if (!LoopInfo)
5762 return LoopInfo.takeError();
5763 Result.push_back(*LoopInfo);
5764 Builder.restoreIP((*LoopInfo)->getAfterIP());
5765 return Error::success();
5766 };
5767 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5769 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5770 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5771 if (!LoopInfo)
5772 return LoopInfo.takeError();
5773 Result.push_back(*LoopInfo);
5774 Builder.restoreIP((*LoopInfo)->getAfterIP());
5775 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5776 return Error::success();
5777 };
5778 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5779 if (Err)
5780 return Err;
5781 return Result;
5782}
5783
5785 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5786 bool IsSigned, bool InclusiveStop, const Twine &Name) {
5787
5788 // Consider the following difficulties (assuming 8-bit signed integers):
5789 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
5790 // DO I = 1, 100, 50
5791 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
5792 // DO I = 100, 0, -128
5793
5794 // Start, Stop and Step must be of the same integer type.
5795 auto *IndVarTy = cast<IntegerType>(Start->getType());
5796 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
5797 assert(IndVarTy == Step->getType() && "Step type mismatch");
5798
5800
5801 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
5802 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
5803
5804 // Like Step, but always positive.
5805 Value *Incr = Step;
5806
5807 // Distance between Start and Stop; always positive.
5808 Value *Span;
5809
5810 // Condition whether there are no iterations are executed at all, e.g. because
5811 // UB < LB.
5812 Value *ZeroCmp;
5813
5814 if (IsSigned) {
5815 // Ensure that increment is positive. If not, negate and invert LB and UB.
5816 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
5817 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
5818 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
5819 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
5820 Span = Builder.CreateSub(UB, LB, "", false, true);
5821 ZeroCmp = Builder.CreateICmp(
5822 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
5823 } else {
5824 Span = Builder.CreateSub(Stop, Start, "", true);
5825 ZeroCmp = Builder.CreateICmp(
5826 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
5827 }
5828
5829 Value *CountIfLooping;
5830 if (InclusiveStop) {
5831 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
5832 } else {
5833 // Avoid incrementing past stop since it could overflow.
5834 Value *CountIfTwo = Builder.CreateAdd(
5835 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
5836 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
5837 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
5838 }
5839
5840 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
5841 "omp_" + Name + ".tripcount");
5842}
5843
5846 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5847 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
5848 ScanInfo *ScanRedInfo) {
5849 LocationDescription ComputeLoc =
5850 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5851
5853 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5854
5855 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5856 Builder.restoreIP(CodeGenIP);
5857 Value *Span = Builder.CreateMul(IV, Step);
5858 Value *IndVar = Builder.CreateAdd(Span, Start);
5859 if (InScan)
5860 ScanRedInfo->IV = IndVar;
5861 return BodyGenCB(Builder.saveIP(), IndVar);
5862 };
5863 LocationDescription LoopLoc =
5864 ComputeIP.isSet()
5865 ? Loc
5866 : LocationDescription(Builder.saveIP(),
5867 Builder.getCurrentDebugLocation());
5868 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
5869}
5870
5871// Returns an LLVM function to call for initializing loop bounds using OpenMP
5872// static scheduling for composite `distribute parallel for` depending on
5873// `type`. Only i32 and i64 are supported by the runtime. Always interpret
5874// integers as unsigned similarly to CanonicalLoopInfo.
5875static FunctionCallee
5877 OpenMPIRBuilder &OMPBuilder) {
5878 unsigned Bitwidth = Ty->getIntegerBitWidth();
5879 if (Bitwidth == 32)
5880 return OMPBuilder.getOrCreateRuntimeFunction(
5881 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
5882 if (Bitwidth == 64)
5883 return OMPBuilder.getOrCreateRuntimeFunction(
5884 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
5885 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
5886}
5887
5888// Returns an LLVM function to call for initializing loop bounds using OpenMP
5889// static scheduling depending on `type`. Only i32 and i64 are supported by the
5890// runtime. Always interpret integers as unsigned similarly to
5891// CanonicalLoopInfo.
5893 OpenMPIRBuilder &OMPBuilder) {
5894 unsigned Bitwidth = Ty->getIntegerBitWidth();
5895 if (Bitwidth == 32)
5896 return OMPBuilder.getOrCreateRuntimeFunction(
5897 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
5898 if (Bitwidth == 64)
5899 return OMPBuilder.getOrCreateRuntimeFunction(
5900 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
5901 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
5902}
5903
5904OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
5905 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
5906 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
5907 OMPScheduleType DistScheduleSchedType) {
5908 assert(CLI->isValid() && "Requires a valid canonical loop");
5909 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
5910 "Require dedicated allocate IP");
5911
5912 // Set up the source location value for OpenMP runtime.
5913 Builder.restoreIP(CLI->getPreheaderIP());
5914 Builder.SetCurrentDebugLocation(DL);
5915
5916 uint32_t SrcLocStrSize;
5917 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
5919 switch (LoopType) {
5920 case WorksharingLoopType::ForStaticLoop:
5921 Flag = OMP_IDENT_FLAG_WORK_LOOP;
5922 break;
5923 case WorksharingLoopType::DistributeStaticLoop:
5924 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
5925 break;
5926 case WorksharingLoopType::DistributeForStaticLoop:
5927 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
5928 break;
5929 }
5930 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
5931
5932 // Declare useful OpenMP runtime functions.
5933 Value *IV = CLI->getIndVar();
5934 Type *IVTy = IV->getType();
5935 FunctionCallee StaticInit =
5936 LoopType == WorksharingLoopType::DistributeForStaticLoop
5937 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
5938 : getKmpcForStaticInitForType(IVTy, M, *this);
5939 FunctionCallee StaticFini =
5940 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
5941
5942 // Allocate space for computed loop bounds as expected by the "init" function.
5943 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
5944
5945 Type *I32Type = Type::getInt32Ty(M.getContext());
5946 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
5947 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
5948 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
5949 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
5950 CLI->setLastIter(PLastIter);
5951
5952 // At the end of the preheader, prepare for calling the "init" function by
5953 // storing the current loop bounds into the allocated space. A canonical loop
5954 // always iterates from 0 to trip-count with step 1. Note that "init" expects
5955 // and produces an inclusive upper bound.
5956 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
5957 Constant *Zero = ConstantInt::get(IVTy, 0);
5958 Constant *One = ConstantInt::get(IVTy, 1);
5959 Builder.CreateStore(Zero, PLowerBound);
5960 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
5961 Builder.CreateStore(UpperBound, PUpperBound);
5962 Builder.CreateStore(One, PStride);
5963
5964 Value *ThreadNum =
5965 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
5966
5967 OMPScheduleType SchedType =
5968 (LoopType == WorksharingLoopType::DistributeStaticLoop)
5969 ? OMPScheduleType::OrderedDistribute
5971 Constant *SchedulingType =
5972 ConstantInt::get(I32Type, static_cast<int>(SchedType));
5973
5974 // Call the "init" function and update the trip count of the loop with the
5975 // value it produced.
5976 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
5977 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
5978 this](Value *SchedulingType, auto &Builder) {
5979 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
5980 PLowerBound, PUpperBound});
5981 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
5982 Value *PDistUpperBound =
5983 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
5984 Args.push_back(PDistUpperBound);
5985 }
5986 Args.append({PStride, One, Zero});
5987 createRuntimeFunctionCall(StaticInit, Args);
5988 };
5989 BuildInitCall(SchedulingType, Builder);
5990 if (HasDistSchedule &&
5991 LoopType != WorksharingLoopType::DistributeStaticLoop) {
5992 Constant *DistScheduleSchedType = ConstantInt::get(
5993 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
5994 // We want to emit a second init function call for the dist_schedule clause
5995 // to the Distribute construct. This should only be done however if a
5996 // Workshare Loop is nested within a Distribute Construct
5997 BuildInitCall(DistScheduleSchedType, Builder);
5998 }
5999 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6000 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6001 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6002 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6003 CLI->setTripCount(TripCount);
6004
6005 // Update all uses of the induction variable except the one in the condition
6006 // block that compares it with the actual upper bound, and the increment in
6007 // the latch block.
6008
6009 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6010 Builder.SetInsertPoint(CLI->getBody(),
6011 CLI->getBody()->getFirstInsertionPt());
6012 Builder.SetCurrentDebugLocation(DL);
6013 return Builder.CreateAdd(OldIV, LowerBound);
6014 });
6015
6016 // In the "exit" block, call the "fini" function.
6017 Builder.SetInsertPoint(CLI->getExit(),
6018 CLI->getExit()->getTerminator()->getIterator());
6019 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6020
6021 // Add the barrier if requested.
6022 if (NeedsBarrier) {
6023 InsertPointOrErrorTy BarrierIP =
6025 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6026 /* CheckCancelFlag */ false);
6027 if (!BarrierIP)
6028 return BarrierIP.takeError();
6029 }
6030
6031 InsertPointTy AfterIP = CLI->getAfterIP();
6032 CLI->invalidate();
6033
6034 return AfterIP;
6035}
6036
6037static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6038 LoopInfo &LI);
6039static void addLoopMetadata(CanonicalLoopInfo *Loop,
6040 ArrayRef<Metadata *> Properties);
6041
6043 LLVMContext &Ctx, Loop *Loop,
6045 SmallVector<Metadata *> &LoopMDList) {
6046 SmallSet<BasicBlock *, 8> Reachable;
6047
6048 // Get the basic blocks from the loop in which memref instructions
6049 // can be found.
6050 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6051 // preferably without running any passes.
6052 for (BasicBlock *Block : Loop->getBlocks()) {
6053 if (Block == CLI->getCond() || Block == CLI->getHeader())
6054 continue;
6055 Reachable.insert(Block);
6056 }
6057
6058 // Add access group metadata to memory-access instructions.
6059 MDNode *AccessGroup = MDNode::getDistinct(Ctx, {});
6060 for (BasicBlock *BB : Reachable)
6061 addAccessGroupMetadata(BB, AccessGroup, LoopInfo);
6062 // TODO: If the loop has existing parallel access metadata, have
6063 // to combine two lists.
6064 LoopMDList.push_back(MDNode::get(
6065 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6066}
6067
6069OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6070 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6071 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6072 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6073 assert(CLI->isValid() && "Requires a valid canonical loop");
6074 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6075
6076 LLVMContext &Ctx = CLI->getFunction()->getContext();
6077 Value *IV = CLI->getIndVar();
6078 Value *OrigTripCount = CLI->getTripCount();
6079 Type *IVTy = IV->getType();
6080 assert(IVTy->getIntegerBitWidth() <= 64 &&
6081 "Max supported tripcount bitwidth is 64 bits");
6082 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6083 : Type::getInt64Ty(Ctx);
6084 Type *I32Type = Type::getInt32Ty(M.getContext());
6085 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6086 Constant *One = ConstantInt::get(InternalIVTy, 1);
6087
6088 Function *F = CLI->getFunction();
6089 // Blocks must have terminators.
6090 // FIXME: Don't run analyses on incomplete/invalid IR.
6091 SmallVector<Instruction *> UIs;
6092 for (BasicBlock &BB : *F)
6093 if (!BB.hasTerminator())
6094 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6096 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6097 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6098 LoopAnalysis LIA;
6099 LoopInfo &&LI = LIA.run(*F, FAM);
6100 for (Instruction *I : UIs)
6101 I->eraseFromParent();
6102 Loop *L = LI.getLoopFor(CLI->getHeader());
6103 SmallVector<Metadata *> LoopMDList;
6104 if (ChunkSize || DistScheduleChunkSize)
6105 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6106 addLoopMetadata(CLI, LoopMDList);
6107
6108 // Declare useful OpenMP runtime functions.
6109 FunctionCallee StaticInit =
6110 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6111 FunctionCallee StaticFini =
6112 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6113
6114 // Allocate space for computed loop bounds as expected by the "init" function.
6115 Builder.restoreIP(AllocaIP);
6116 Builder.SetCurrentDebugLocation(DL);
6117 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6118 Value *PLowerBound =
6119 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6120 Value *PUpperBound =
6121 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6122 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6123 CLI->setLastIter(PLastIter);
6124
6125 // Set up the source location value for the OpenMP runtime.
6126 Builder.restoreIP(CLI->getPreheaderIP());
6127 Builder.SetCurrentDebugLocation(DL);
6128
6129 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6130 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6131 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6132 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6133 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6134 "distschedulechunksize");
6135 Value *CastedTripCount =
6136 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6137
6138 Constant *SchedulingType =
6139 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6140 Constant *DistSchedulingType =
6141 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6142 Builder.CreateStore(Zero, PLowerBound);
6143 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6144 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6145 Value *UpperBound =
6146 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6147 Builder.CreateStore(UpperBound, PUpperBound);
6148 Builder.CreateStore(One, PStride);
6149
6150 // Call the "init" function and update the trip count of the loop with the
6151 // value it produced.
6152 uint32_t SrcLocStrSize;
6153 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6154 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6155 if (DistScheduleSchedType != OMPScheduleType::None) {
6156 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6157 }
6158 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6159 Value *ThreadNum =
6160 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6161 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6162 PUpperBound, PStride, One,
6163 this](Value *SchedulingType, Value *ChunkSize,
6164 auto &Builder) {
6166 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6167 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6168 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6169 /*pstride=*/PStride, /*incr=*/One,
6170 /*chunk=*/ChunkSize});
6171 };
6172 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6173 if (DistScheduleSchedType != OMPScheduleType::None &&
6174 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6175 SchedType != OMPScheduleType::OrderedDistribute) {
6176 // We want to emit a second init function call for the dist_schedule clause
6177 // to the Distribute construct. This should only be done however if a
6178 // Workshare Loop is nested within a Distribute Construct
6179 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6180 }
6181
6182 // Load values written by the "init" function.
6183 Value *FirstChunkStart =
6184 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6185 Value *FirstChunkStop =
6186 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6187 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6188 Value *ChunkRange =
6189 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6190 Value *NextChunkStride =
6191 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6192
6193 // Create outer "dispatch" loop for enumerating the chunks.
6194 BasicBlock *DispatchEnter = splitBB(Builder, true);
6195 Value *DispatchCounter;
6196
6197 // It is safe to assume this didn't return an error because the callback
6198 // passed into createCanonicalLoop is the only possible error source, and it
6199 // always returns success.
6200 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6201 {Builder.saveIP(), DL},
6202 [&](InsertPointTy BodyIP, Value *Counter) {
6203 DispatchCounter = Counter;
6204 return Error::success();
6205 },
6206 FirstChunkStart, CastedTripCount, NextChunkStride,
6207 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6208 "dispatch"));
6209
6210 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6211 // not have to preserve the canonical invariant.
6212 BasicBlock *DispatchBody = DispatchCLI->getBody();
6213 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6214 BasicBlock *DispatchExit = DispatchCLI->getExit();
6215 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6216 DispatchCLI->invalidate();
6217
6218 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6219 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6220 redirectTo(CLI->getExit(), DispatchLatch, DL);
6221 redirectTo(DispatchBody, DispatchEnter, DL);
6222
6223 // Prepare the prolog of the chunk loop.
6224 Builder.restoreIP(CLI->getPreheaderIP());
6225 Builder.SetCurrentDebugLocation(DL);
6226
6227 // Compute the number of iterations of the chunk loop.
6228 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6229 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6230 Value *IsLastChunk =
6231 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6232 Value *CountUntilOrigTripCount =
6233 Builder.CreateSub(CastedTripCount, DispatchCounter);
6234 Value *ChunkTripCount = Builder.CreateSelect(
6235 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6236 Value *BackcastedChunkTC =
6237 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6238 CLI->setTripCount(BackcastedChunkTC);
6239
6240 // Update all uses of the induction variable except the one in the condition
6241 // block that compares it with the actual upper bound, and the increment in
6242 // the latch block.
6243 Value *BackcastedDispatchCounter =
6244 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6245 CLI->mapIndVar([&](Instruction *) -> Value * {
6246 Builder.restoreIP(CLI->getBodyIP());
6247 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6248 });
6249
6250 // In the "exit" block, call the "fini" function.
6251 Builder.SetInsertPoint(DispatchExit, DispatchExit->getFirstInsertionPt());
6252 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6253
6254 // Add the barrier if requested.
6255 if (NeedsBarrier) {
6256 InsertPointOrErrorTy AfterIP =
6257 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6258 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6259 if (!AfterIP)
6260 return AfterIP.takeError();
6261 }
6262
6263#ifndef NDEBUG
6264 // Even though we currently do not support applying additional methods to it,
6265 // the chunk loop should remain a canonical loop.
6266 CLI->assertOK();
6267#endif
6268
6269 return InsertPointTy(DispatchAfter, DispatchAfter->getFirstInsertionPt());
6270}
6271
6272// Returns an LLVM function to call for executing an OpenMP static worksharing
6273// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6274// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6275static FunctionCallee
6277 WorksharingLoopType LoopType) {
6278 unsigned Bitwidth = Ty->getIntegerBitWidth();
6279 Module &M = OMPBuilder->M;
6280 switch (LoopType) {
6281 case WorksharingLoopType::ForStaticLoop:
6282 if (Bitwidth == 32)
6283 return OMPBuilder->getOrCreateRuntimeFunction(
6284 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6285 if (Bitwidth == 64)
6286 return OMPBuilder->getOrCreateRuntimeFunction(
6287 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6288 break;
6289 case WorksharingLoopType::DistributeStaticLoop:
6290 if (Bitwidth == 32)
6291 return OMPBuilder->getOrCreateRuntimeFunction(
6292 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6293 if (Bitwidth == 64)
6294 return OMPBuilder->getOrCreateRuntimeFunction(
6295 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6296 break;
6297 case WorksharingLoopType::DistributeForStaticLoop:
6298 if (Bitwidth == 32)
6299 return OMPBuilder->getOrCreateRuntimeFunction(
6300 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6301 if (Bitwidth == 64)
6302 return OMPBuilder->getOrCreateRuntimeFunction(
6303 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6304 break;
6305 }
6306 if (Bitwidth != 32 && Bitwidth != 64) {
6307 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6308 }
6309 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6310}
6311
6312// Inserts a call to proper OpenMP Device RTL function which handles
6313// loop worksharing.
6315 WorksharingLoopType LoopType,
6316 BasicBlock *InsertBlock, Value *Ident,
6317 Value *LoopBodyArg, Value *TripCount,
6318 Function &LoopBodyFn, bool NoLoop) {
6319 Type *TripCountTy = TripCount->getType();
6320 Module &M = OMPBuilder->M;
6321 IRBuilder<> &Builder = OMPBuilder->Builder;
6322 FunctionCallee RTLFn =
6323 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6324 SmallVector<Value *, 8> RealArgs;
6325 RealArgs.push_back(Ident);
6326 RealArgs.push_back(&LoopBodyFn);
6327 RealArgs.push_back(LoopBodyArg);
6328 RealArgs.push_back(TripCount);
6329 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6330 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6331 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6332 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6333 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6334 return;
6335 }
6336 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6337 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6338 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6339 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6340
6341 RealArgs.push_back(
6342 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6343 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6344 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6345 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6346 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6347 } else {
6348 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6349 }
6350
6351 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6352}
6353
6355 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6356 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6357 WorksharingLoopType LoopType, bool NoLoop) {
6358 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6359 BasicBlock *Preheader = CLI->getPreheader();
6360 Value *TripCount = CLI->getTripCount();
6361
6362 // After loop body outling, the loop body contains only set up
6363 // of loop body argument structure and the call to the outlined
6364 // loop body function. Firstly, we need to move setup of loop body args
6365 // into loop preheader.
6366 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6367 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6368
6369 // The next step is to remove the whole loop. We do not it need anymore.
6370 // That's why make an unconditional branch from loop preheader to loop
6371 // exit block
6372 Builder.restoreIP({Preheader, Preheader->end()});
6373 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6374 Preheader->getTerminator()->eraseFromParent();
6375 Builder.CreateBr(CLI->getExit());
6376
6377 // Delete dead loop blocks
6378 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6379 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6380 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6381 CleanUpInfo.EntryBB = CLI->getHeader();
6382 CleanUpInfo.ExitBB = CLI->getExit();
6383 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6384 DeleteDeadBlocks(BlocksToBeRemoved);
6385
6386 // Find the instruction which corresponds to loop body argument structure
6387 // and remove the call to loop body function instruction.
6388 Value *LoopBodyArg;
6389 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6390 assert(OutlinedFnUser &&
6391 "Expected unique undroppable user of outlined function");
6392 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6393 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6394 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6395 "Expected outlined function call to be located in loop preheader");
6396 // Check in case no argument structure has been passed.
6397 if (OutlinedFnCallInstruction->arg_size() > 1)
6398 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6399 else
6400 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6401 OutlinedFnCallInstruction->eraseFromParent();
6402
6403 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6404 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6405
6406 for (auto &ToBeDeletedItem : ToBeDeleted)
6407 ToBeDeletedItem->eraseFromParent();
6408 CLI->invalidate();
6409}
6410
6411OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6412 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6413 WorksharingLoopType LoopType, bool NoLoop) {
6414 uint32_t SrcLocStrSize;
6415 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6417 switch (LoopType) {
6418 case WorksharingLoopType::ForStaticLoop:
6419 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6420 break;
6421 case WorksharingLoopType::DistributeStaticLoop:
6422 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6423 break;
6424 case WorksharingLoopType::DistributeForStaticLoop:
6425 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6426 break;
6427 }
6428 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6429
6430 auto OI = std::make_unique<OutlineInfo>();
6431 OI->OuterAllocBB = CLI->getPreheader();
6432 Function *OuterFn = CLI->getPreheader()->getParent();
6433
6434 // Instructions which need to be deleted at the end of code generation
6435 SmallVector<Instruction *, 4> ToBeDeleted;
6436
6437 OI->OuterAllocBB = AllocaIP.getBlock();
6438
6439 // Mark the body loop as region which needs to be extracted
6440 OI->EntryBB = CLI->getBody();
6441 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6442 "omp.prelatch");
6443
6444 // Prepare loop body for extraction
6445 Builder.restoreIP({CLI->getPreheader(), CLI->getPreheader()->begin()});
6446
6447 // Insert new loop counter variable which will be used only in loop
6448 // body.
6449 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6450 Instruction *NewLoopCntLoad =
6451 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6452 // New loop counter instructions are redundant in the loop preheader when
6453 // code generation for workshare loop is finshed. That's why mark them as
6454 // ready for deletion.
6455 ToBeDeleted.push_back(NewLoopCntLoad);
6456 ToBeDeleted.push_back(NewLoopCnt);
6457
6458 // Analyse loop body region. Find all input variables which are used inside
6459 // loop body region.
6460 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6462 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6463
6464 CodeExtractorAnalysisCache CEAC(*OuterFn);
6465 CodeExtractor Extractor(Blocks,
6466 /* DominatorTree */ nullptr,
6467 /* AggregateArgs */ true,
6468 /* BlockFrequencyInfo */ nullptr,
6469 /* BranchProbabilityInfo */ nullptr,
6470 /* AssumptionCache */ nullptr,
6471 /* AllowVarArgs */ true,
6472 /* AllowAlloca */ true,
6473 /* AllocationBlock */ CLI->getPreheader(),
6474 /* DeallocationBlocks */ {},
6475 /* Suffix */ ".omp_wsloop",
6476 /* AggrArgsIn0AddrSpace */ true);
6477
6478 BasicBlock *CommonExit = nullptr;
6479 SetVector<Value *> SinkingCands, HoistingCands;
6480
6481 // Find allocas outside the loop body region which are used inside loop
6482 // body
6483 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6484
6485 // We need to model loop body region as the function f(cnt, loop_arg).
6486 // That's why we replace loop induction variable by the new counter
6487 // which will be one of loop body function argument
6489 CLI->getIndVar()->user_end());
6490 for (auto Use : Users) {
6491 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6492 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6493 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6494 }
6495 }
6496 }
6497 // Make sure that loop counter variable is not merged into loop body
6498 // function argument structure and it is passed as separate variable
6499 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6500
6501 // PostOutline CB is invoked when loop body function is outlined and
6502 // loop body is replaced by call to outlined function. We need to add
6503 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6504 // function will handle loop control logic.
6505 //
6506 OI->PostOutlineCB = [=, ToBeDeletedVec =
6507 std::move(ToBeDeleted)](Function &OutlinedFn) {
6508 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6509 LoopType, NoLoop);
6510 };
6511 addOutlineInfo(std::move(OI));
6512 return CLI->getAfterIP();
6513}
6514
6517 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6518 bool HasSimdModifier, bool HasMonotonicModifier,
6519 bool HasNonmonotonicModifier, bool HasOrderedClause,
6520 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6521 Value *DistScheduleChunkSize) {
6522 if (Config.isTargetDevice())
6523 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NoLoop);
6524 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6525 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6526 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6527
6528 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6529 OMPScheduleType::ModifierOrdered;
6530 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6531 if (HasDistSchedule) {
6532 DistScheduleSchedType = DistScheduleChunkSize
6533 ? OMPScheduleType::OrderedDistributeChunked
6534 : OMPScheduleType::OrderedDistribute;
6535 }
6536 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6537 case OMPScheduleType::BaseStatic:
6538 case OMPScheduleType::BaseDistribute:
6539 assert((!ChunkSize || !DistScheduleChunkSize) &&
6540 "No chunk size with static-chunked schedule");
6541 if (IsOrdered && !HasDistSchedule)
6542 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6543 NeedsBarrier, ChunkSize);
6544 // FIXME: Monotonicity ignored?
6545 if (DistScheduleChunkSize)
6546 return applyStaticChunkedWorkshareLoop(
6547 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6548 DistScheduleChunkSize, DistScheduleSchedType);
6549 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6550 HasDistSchedule);
6551
6552 case OMPScheduleType::BaseStaticChunked:
6553 case OMPScheduleType::BaseDistributeChunked:
6554 if (IsOrdered && !HasDistSchedule)
6555 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6556 NeedsBarrier, ChunkSize);
6557 // FIXME: Monotonicity ignored?
6558 return applyStaticChunkedWorkshareLoop(
6559 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6560 DistScheduleChunkSize, DistScheduleSchedType);
6561
6562 case OMPScheduleType::BaseRuntime:
6563 case OMPScheduleType::BaseAuto:
6564 case OMPScheduleType::BaseGreedy:
6565 case OMPScheduleType::BaseBalanced:
6566 case OMPScheduleType::BaseSteal:
6567 case OMPScheduleType::BaseRuntimeSimd:
6568 assert(!ChunkSize &&
6569 "schedule type does not support user-defined chunk sizes");
6570 [[fallthrough]];
6571 case OMPScheduleType::BaseGuidedSimd:
6572 case OMPScheduleType::BaseDynamicChunked:
6573 case OMPScheduleType::BaseGuidedChunked:
6574 case OMPScheduleType::BaseGuidedIterativeChunked:
6575 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6576 case OMPScheduleType::BaseStaticBalancedChunked:
6577 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6578 NeedsBarrier, ChunkSize);
6579
6580 default:
6581 llvm_unreachable("Unknown/unimplemented schedule kind");
6582 }
6583}
6584
6585/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6586/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6587/// the runtime. Always interpret integers as unsigned similarly to
6588/// CanonicalLoopInfo.
6589static FunctionCallee
6591 unsigned Bitwidth = Ty->getIntegerBitWidth();
6592 if (Bitwidth == 32)
6593 return OMPBuilder.getOrCreateRuntimeFunction(
6594 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6595 if (Bitwidth == 64)
6596 return OMPBuilder.getOrCreateRuntimeFunction(
6597 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6598 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6599}
6600
6601/// Returns an LLVM function to call for updating the next loop using OpenMP
6602/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6603/// the runtime. Always interpret integers as unsigned similarly to
6604/// CanonicalLoopInfo.
6605static FunctionCallee
6607 unsigned Bitwidth = Ty->getIntegerBitWidth();
6608 if (Bitwidth == 32)
6609 return OMPBuilder.getOrCreateRuntimeFunction(
6610 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6611 if (Bitwidth == 64)
6612 return OMPBuilder.getOrCreateRuntimeFunction(
6613 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6614 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6615}
6616
6617/// Returns an LLVM function to call for finalizing the dynamic loop using
6618/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6619/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6620static FunctionCallee
6622 unsigned Bitwidth = Ty->getIntegerBitWidth();
6623 if (Bitwidth == 32)
6624 return OMPBuilder.getOrCreateRuntimeFunction(
6625 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6626 if (Bitwidth == 64)
6627 return OMPBuilder.getOrCreateRuntimeFunction(
6628 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6629 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6630}
6631
6633OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6634 InsertPointTy AllocaIP,
6635 OMPScheduleType SchedType,
6636 bool NeedsBarrier, Value *Chunk) {
6637 assert(CLI->isValid() && "Requires a valid canonical loop");
6638 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6639 "Require dedicated allocate IP");
6641 "Require valid schedule type");
6642
6643 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6644 OMPScheduleType::ModifierOrdered;
6645
6646 // Set up the source location value for OpenMP runtime.
6647 Builder.SetCurrentDebugLocation(DL);
6648
6649 uint32_t SrcLocStrSize;
6650 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6651 Value *SrcLoc =
6652 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6653
6654 // Declare useful OpenMP runtime functions.
6655 Value *IV = CLI->getIndVar();
6656 Type *IVTy = IV->getType();
6657 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6658 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6659
6660 // Allocate space for computed loop bounds as expected by the "init" function.
6661 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6662 Type *I32Type = Type::getInt32Ty(M.getContext());
6663 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6664 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6665 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6666 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6667 CLI->setLastIter(PLastIter);
6668
6669 // At the end of the preheader, prepare for calling the "init" function by
6670 // storing the current loop bounds into the allocated space. A canonical loop
6671 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6672 // and produces an inclusive upper bound.
6673 BasicBlock *PreHeader = CLI->getPreheader();
6674 Builder.SetInsertPoint(PreHeader->getTerminator());
6675 Constant *One = ConstantInt::get(IVTy, 1);
6676 Builder.CreateStore(One, PLowerBound);
6677 Value *UpperBound = CLI->getTripCount();
6678 Builder.CreateStore(UpperBound, PUpperBound);
6679 Builder.CreateStore(One, PStride);
6680
6681 BasicBlock *Header = CLI->getHeader();
6682 BasicBlock *Exit = CLI->getExit();
6683 BasicBlock *Cond = CLI->getCond();
6684 BasicBlock *Latch = CLI->getLatch();
6685 InsertPointTy AfterIP = CLI->getAfterIP();
6686
6687 // The CLI will be "broken" in the code below, as the loop is no longer
6688 // a valid canonical loop.
6689
6690 if (!Chunk)
6691 Chunk = One;
6692
6693 Value *ThreadNum =
6694 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6695
6696 Constant *SchedulingType =
6697 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6698
6699 // Call the "init" function.
6700 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6701 /* LowerBound */ One, UpperBound,
6702 /* step */ One, Chunk});
6703
6704 // An outer loop around the existing one.
6705 BasicBlock *OuterCond = BasicBlock::Create(
6706 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6707 PreHeader->getParent());
6708 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6709 Builder.SetInsertPoint(OuterCond, OuterCond->getFirstInsertionPt());
6711 DynamicNext,
6712 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6713 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6714 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6715 Value *LowerBound =
6716 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6717 Builder.CreateCondBr(MoreWork, Header, Exit);
6718
6719 // Change PHI-node in loop header to use outer cond rather than preheader,
6720 // and set IV to the LowerBound.
6721 Instruction *Phi = &Header->front();
6722 auto *PI = cast<PHINode>(Phi);
6723 PI->setIncomingBlock(0, OuterCond);
6724 PI->setIncomingValue(0, LowerBound);
6725
6726 // Then set the pre-header to jump to the OuterCond
6727 Instruction *Term = PreHeader->getTerminator();
6728 auto *Br = cast<UncondBrInst>(Term);
6729 Br->setSuccessor(OuterCond);
6730
6731 // Modify the inner condition:
6732 // * Use the UpperBound returned from the DynamicNext call.
6733 // * jump to the loop outer loop when done with one of the inner loops.
6734 Builder.SetInsertPoint(Cond, Cond->getFirstInsertionPt());
6735 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
6736 Instruction *Comp = &*Builder.GetInsertPoint();
6737 auto *CI = cast<CmpInst>(Comp);
6738 CI->setOperand(1, UpperBound);
6739 // Redirect the inner exit to branch to outer condition.
6740 Instruction *Branch = &Cond->back();
6741 auto *BI = cast<CondBrInst>(Branch);
6742 assert(BI->getSuccessor(1) == Exit);
6743 BI->setSuccessor(1, OuterCond);
6744
6745 // Call the "fini" function if "ordered" is present in wsloop directive.
6746 if (Ordered) {
6747 Builder.SetInsertPoint(&Latch->back());
6748 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
6749 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
6750 }
6751
6752 // Add the barrier if requested.
6753 if (NeedsBarrier) {
6754 Builder.SetInsertPoint(&Exit->back());
6755 InsertPointOrErrorTy BarrierIP =
6757 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6758 /* CheckCancelFlag */ false);
6759 if (!BarrierIP)
6760 return BarrierIP.takeError();
6761 }
6762
6763 CLI->invalidate();
6764 return AfterIP;
6765}
6766
6767/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
6768/// after this \p OldTarget will be orphaned.
6770 BasicBlock *NewTarget, DebugLoc DL) {
6771 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
6772 redirectTo(Pred, NewTarget, DL);
6773}
6774
6776 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
6777 // We add a block to BBsToKeep iff we have proven it has an external use.
6779
6780 while (true) {
6781 bool Changed = false;
6782
6783 for (BasicBlock *BB : BBs) {
6784 if (BBsToKeep.contains(BB))
6785 continue;
6786
6787 for (Use &U : BB->uses()) {
6788 auto *UseInst = dyn_cast<Instruction>(U.getUser());
6789 if (!UseInst)
6790 continue;
6791 BasicBlock *UseBB = UseInst->getParent();
6792 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
6793 BBsToKeep.insert(BB);
6794 Changed = true;
6795 break;
6796 }
6797 }
6798 }
6799
6800 if (!Changed)
6801 break;
6802 }
6803
6805 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
6806 DeleteDeadBlocks(BBsToDelete);
6807}
6808
6809CanonicalLoopInfo *
6811 InsertPointTy ComputeIP) {
6812 assert(Loops.size() >= 1 && "At least one loop required");
6813 size_t NumLoops = Loops.size();
6814
6815 // Nothing to do if there is already just one loop.
6816 if (NumLoops == 1)
6817 return Loops.front();
6818
6819 CanonicalLoopInfo *Outermost = Loops.front();
6820 CanonicalLoopInfo *Innermost = Loops.back();
6821 BasicBlock *OrigPreheader = Outermost->getPreheader();
6822 BasicBlock *OrigAfter = Outermost->getAfter();
6823 Function *F = OrigPreheader->getParent();
6824
6825 // Loop control blocks that may become orphaned later.
6826 SmallVector<BasicBlock *, 12> OldControlBBs;
6827 OldControlBBs.reserve(6 * Loops.size());
6829 Loop->collectControlBlocks(OldControlBBs);
6830
6831 // Setup the IRBuilder for inserting the trip count computation.
6832 Builder.SetCurrentDebugLocation(DL);
6833 if (ComputeIP.isSet())
6834 Builder.restoreIP(ComputeIP);
6835 else
6836 Builder.restoreIP(Outermost->getPreheaderIP());
6837
6838 // Derive the collapsed' loop trip count.
6839 // TODO: Find common/largest indvar type.
6840 Value *CollapsedTripCount = nullptr;
6841 for (CanonicalLoopInfo *L : Loops) {
6842 assert(L->isValid() &&
6843 "All loops to collapse must be valid canonical loops");
6844 Value *OrigTripCount = L->getTripCount();
6845 if (!CollapsedTripCount) {
6846 CollapsedTripCount = OrigTripCount;
6847 continue;
6848 }
6849
6850 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
6851 CollapsedTripCount =
6852 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
6853 }
6854
6855 // Create the collapsed loop control flow.
6856 CanonicalLoopInfo *Result =
6857 createLoopSkeleton(DL, CollapsedTripCount, F,
6858 OrigPreheader->getNextNode(), OrigAfter, "collapsed");
6859
6860 // Build the collapsed loop body code.
6861 // Start with deriving the input loop induction variables from the collapsed
6862 // one, using a divmod scheme. To preserve the original loops' order, the
6863 // innermost loop use the least significant bits.
6864 Builder.restoreIP(Result->getBodyIP());
6865
6866 Value *Leftover = Result->getIndVar();
6867 SmallVector<Value *> NewIndVars;
6868 NewIndVars.resize(NumLoops);
6869 for (int i = NumLoops - 1; i >= 1; --i) {
6870 Value *OrigTripCount = Loops[i]->getTripCount();
6871
6872 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
6873 NewIndVars[i] = NewIndVar;
6874
6875 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
6876 }
6877 // Outermost loop gets all the remaining bits.
6878 NewIndVars[0] = Leftover;
6879
6880 // Construct the loop body control flow.
6881 // We progressively construct the branch structure following in direction of
6882 // the control flow, from the leading in-between code, the loop nest body, the
6883 // trailing in-between code, and rejoining the collapsed loop's latch.
6884 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
6885 // the ContinueBlock is set, continue with that block. If ContinuePred, use
6886 // its predecessors as sources.
6887 BasicBlock *ContinueBlock = Result->getBody();
6888 BasicBlock *ContinuePred = nullptr;
6889 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
6890 BasicBlock *NextSrc) {
6891 if (ContinueBlock)
6892 redirectTo(ContinueBlock, Dest, DL);
6893 else
6894 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
6895
6896 ContinueBlock = nullptr;
6897 ContinuePred = NextSrc;
6898 };
6899
6900 // The code before the nested loop of each level.
6901 // Because we are sinking it into the nest, it will be executed more often
6902 // that the original loop. More sophisticated schemes could keep track of what
6903 // the in-between code is and instantiate it only once per thread.
6904 for (size_t i = 0; i < NumLoops - 1; ++i)
6905 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
6906
6907 // Connect the loop nest body.
6908 ContinueWith(Innermost->getBody(), Innermost->getLatch());
6909
6910 // The code after the nested loop at each level.
6911 for (size_t i = NumLoops - 1; i > 0; --i)
6912 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
6913
6914 // Connect the finished loop to the collapsed loop latch.
6915 ContinueWith(Result->getLatch(), nullptr);
6916
6917 // Replace the input loops with the new collapsed loop.
6918 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
6919 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
6920
6921 // Replace the input loop indvars with the derived ones.
6922 for (size_t i = 0; i < NumLoops; ++i)
6923 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
6924
6925 // Remove unused parts of the input loops.
6926 removeUnusedBlocksFromParent(OldControlBBs);
6927
6928 for (CanonicalLoopInfo *L : Loops)
6929 L->invalidate();
6930
6931#ifndef NDEBUG
6932 Result->assertOK();
6933#endif
6934 return Result;
6935}
6936
6937std::vector<CanonicalLoopInfo *>
6939 ArrayRef<Value *> TileSizes) {
6940 assert(TileSizes.size() == Loops.size() &&
6941 "Must pass as many tile sizes as there are loops");
6942 int NumLoops = Loops.size();
6943 assert(NumLoops >= 1 && "At least one loop to tile required");
6944
6945 CanonicalLoopInfo *OutermostLoop = Loops.front();
6946 CanonicalLoopInfo *InnermostLoop = Loops.back();
6947 Function *F = OutermostLoop->getBody()->getParent();
6948 BasicBlock *InnerEnter = InnermostLoop->getBody();
6949 BasicBlock *InnerLatch = InnermostLoop->getLatch();
6950
6951 // Loop control blocks that may become orphaned later.
6952 SmallVector<BasicBlock *, 12> OldControlBBs;
6953 OldControlBBs.reserve(6 * Loops.size());
6955 Loop->collectControlBlocks(OldControlBBs);
6956
6957 // Collect original trip counts and induction variable to be accessible by
6958 // index. Also, the structure of the original loops is not preserved during
6959 // the construction of the tiled loops, so do it before we scavenge the BBs of
6960 // any original CanonicalLoopInfo.
6961 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
6962 for (CanonicalLoopInfo *L : Loops) {
6963 assert(L->isValid() && "All input loops must be valid canonical loops");
6964 OrigTripCounts.push_back(L->getTripCount());
6965 OrigIndVars.push_back(L->getIndVar());
6966 }
6967
6968 // Collect the code between loop headers. These may contain SSA definitions
6969 // that are used in the loop nest body. To be usable with in the innermost
6970 // body, these BasicBlocks will be sunk into the loop nest body. That is,
6971 // these instructions may be executed more often than before the tiling.
6972 // TODO: It would be sufficient to only sink them into body of the
6973 // corresponding tile loop.
6975 for (int i = 0; i < NumLoops - 1; ++i) {
6976 CanonicalLoopInfo *Surrounding = Loops[i];
6977 CanonicalLoopInfo *Nested = Loops[i + 1];
6978
6979 BasicBlock *EnterBB = Surrounding->getBody();
6980 BasicBlock *ExitBB = Nested->getHeader();
6981 InbetweenCode.emplace_back(EnterBB, ExitBB);
6982 }
6983
6984 // Compute the trip counts of the floor loops.
6985 Builder.SetCurrentDebugLocation(DL);
6986 Builder.restoreIP(OutermostLoop->getPreheaderIP());
6987 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
6988 for (int i = 0; i < NumLoops; ++i) {
6989 Value *TileSize = TileSizes[i];
6990 Value *OrigTripCount = OrigTripCounts[i];
6991 Type *IVType = OrigTripCount->getType();
6992
6993 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
6994 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
6995
6996 // 0 if tripcount divides the tilesize, 1 otherwise.
6997 // 1 means we need an additional iteration for a partial tile.
6998 //
6999 // Unfortunately we cannot just use the roundup-formula
7000 // (tripcount + tilesize - 1)/tilesize
7001 // because the summation might overflow. We do not want introduce undefined
7002 // behavior when the untiled loop nest did not.
7003 Value *FloorTripOverflow =
7004 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7005
7006 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7007 Value *FloorTripCount =
7008 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7009 "omp_floor" + Twine(i) + ".tripcount", true);
7010
7011 // Remember some values for later use.
7012 FloorCompleteCount.push_back(FloorCompleteTripCount);
7013 FloorCount.push_back(FloorTripCount);
7014 FloorRems.push_back(FloorTripRem);
7015 }
7016
7017 // Generate the new loop nest, from the outermost to the innermost.
7018 std::vector<CanonicalLoopInfo *> Result;
7019 Result.reserve(NumLoops * 2);
7020
7021 // The basic block of the surrounding loop that enters the nest generated
7022 // loop.
7023 BasicBlock *Enter = OutermostLoop->getPreheader();
7024
7025 // The basic block of the surrounding loop where the inner code should
7026 // continue.
7027 BasicBlock *Continue = OutermostLoop->getAfter();
7028
7029 // Where the next loop basic block should be inserted.
7030 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7031
7032 auto EmbeddNewLoop =
7033 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7034 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7035 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7036 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7037 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7038 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7039
7040 // Setup the position where the next embedded loop connects to this loop.
7041 Enter = EmbeddedLoop->getBody();
7042 Continue = EmbeddedLoop->getLatch();
7043 OutroInsertBefore = EmbeddedLoop->getLatch();
7044 return EmbeddedLoop;
7045 };
7046
7047 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7048 const Twine &NameBase) {
7049 for (auto P : enumerate(TripCounts)) {
7050 CanonicalLoopInfo *EmbeddedLoop =
7051 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7052 Result.push_back(EmbeddedLoop);
7053 }
7054 };
7055
7056 EmbeddNewLoops(FloorCount, "floor");
7057
7058 // Within the innermost floor loop, emit the code that computes the tile
7059 // sizes.
7060 Builder.SetInsertPoint(Enter->getTerminator());
7061 SmallVector<Value *, 4> TileCounts;
7062 for (int i = 0; i < NumLoops; ++i) {
7063 CanonicalLoopInfo *FloorLoop = Result[i];
7064 Value *TileSize = TileSizes[i];
7065
7066 Value *FloorIsEpilogue =
7067 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7068 Value *TileTripCount =
7069 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7070
7071 TileCounts.push_back(TileTripCount);
7072 }
7073
7074 // Create the tile loops.
7075 EmbeddNewLoops(TileCounts, "tile");
7076
7077 // Insert the inbetween code into the body.
7078 BasicBlock *BodyEnter = Enter;
7079 BasicBlock *BodyEntered = nullptr;
7080 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7081 BasicBlock *EnterBB = P.first;
7082 BasicBlock *ExitBB = P.second;
7083
7084 if (BodyEnter)
7085 redirectTo(BodyEnter, EnterBB, DL);
7086 else
7087 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7088
7089 BodyEnter = nullptr;
7090 BodyEntered = ExitBB;
7091 }
7092
7093 // Append the original loop nest body into the generated loop nest body.
7094 if (BodyEnter)
7095 redirectTo(BodyEnter, InnerEnter, DL);
7096 else
7097 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7099
7100 // Replace the original induction variable with an induction variable computed
7101 // from the tile and floor induction variables.
7102 Builder.restoreIP(Result.back()->getBodyIP());
7103 for (int i = 0; i < NumLoops; ++i) {
7104 CanonicalLoopInfo *FloorLoop = Result[i];
7105 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7106 Value *OrigIndVar = OrigIndVars[i];
7107 Value *Size = TileSizes[i];
7108
7109 Value *Scale =
7110 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7111 Value *Shift =
7112 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7113 OrigIndVar->replaceAllUsesWith(Shift);
7114 }
7115
7116 // Remove unused parts of the original loops.
7117 removeUnusedBlocksFromParent(OldControlBBs);
7118
7119 for (CanonicalLoopInfo *L : Loops)
7120 L->invalidate();
7121
7122#ifndef NDEBUG
7123 for (CanonicalLoopInfo *GenL : Result)
7124 GenL->assertOK();
7125#endif
7126 return Result;
7127}
7128
7129/// Attach metadata \p Properties to the basic block described by \p BB. If the
7130/// basic block already has metadata, the basic block properties are appended.
7132 ArrayRef<Metadata *> Properties) {
7133 // Nothing to do if no property to attach.
7134 if (Properties.empty())
7135 return;
7136
7137 LLVMContext &Ctx = BB->getContext();
7138 SmallVector<Metadata *> NewProperties;
7139 NewProperties.push_back(nullptr);
7140
7141 // If the basic block already has metadata, prepend it to the new metadata.
7142 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7143 if (Existing)
7144 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7145
7146 append_range(NewProperties, Properties);
7147 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7148 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7149
7150 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7151}
7152
7153/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7154/// loop already has metadata, the loop properties are appended.
7156 ArrayRef<Metadata *> Properties) {
7157 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7158
7159 // Attach metadata to the loop's latch
7160 BasicBlock *Latch = Loop->getLatch();
7161 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7162 addBasicBlockMetadata(Latch, Properties);
7163}
7164
7165/// Attach llvm.access.group metadata to the memref instructions of \p Block
7167 LoopInfo &LI) {
7168 for (Instruction &I : *Block) {
7169 if (I.mayReadOrWriteMemory()) {
7170 // TODO: This instruction may already have access group from
7171 // other pragmas e.g. #pragma clang loop vectorize. Append
7172 // so that the existing metadata is not overwritten.
7173 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7174 }
7175 }
7176}
7177
7178CanonicalLoopInfo *
7180 CanonicalLoopInfo *firstLoop = Loops.front();
7181 CanonicalLoopInfo *lastLoop = Loops.back();
7182 Function *F = firstLoop->getPreheader()->getParent();
7183
7184 // Loop control blocks that will become orphaned later
7185 SmallVector<BasicBlock *> oldControlBBs;
7187 Loop->collectControlBlocks(oldControlBBs);
7188
7189 // Collect original trip counts
7190 SmallVector<Value *> origTripCounts;
7191 for (CanonicalLoopInfo *L : Loops) {
7192 assert(L->isValid() && "All input loops must be valid canonical loops");
7193 origTripCounts.push_back(L->getTripCount());
7194 }
7195
7196 Builder.SetCurrentDebugLocation(DL);
7197
7198 // Compute max trip count.
7199 // The fused loop will be from 0 to max(origTripCounts)
7200 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7201 F, firstLoop->getHeader());
7202 Builder.SetInsertPoint(TCBlock);
7203 Value *fusedTripCount = nullptr;
7204 for (CanonicalLoopInfo *L : Loops) {
7205 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7206 Value *origTripCount = L->getTripCount();
7207 if (!fusedTripCount) {
7208 fusedTripCount = origTripCount;
7209 continue;
7210 }
7211 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7212 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7213 ".omp.fuse.tc");
7214 }
7215
7216 // Generate new loop
7217 CanonicalLoopInfo *fused =
7218 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7219 lastLoop->getLatch(), "fused");
7220
7221 // Replace original loops with the fused loop
7222 // Preheader and After are not considered inside the CLI.
7223 // These are used to compute the individual TCs of the loops
7224 // so they have to be put before the resulting fused loop.
7225 // Moving them up for readability.
7226 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7227 Loops[i]->getPreheader()->moveBefore(TCBlock);
7228 Loops[i]->getAfter()->moveBefore(TCBlock);
7229 }
7230 lastLoop->getPreheader()->moveBefore(TCBlock);
7231
7232 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7233 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7234 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7235 }
7236 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7237 redirectTo(TCBlock, fused->getPreheader(), DL);
7238 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7239
7240 // Build the fused body
7241 // Create new Blocks with conditions that jump to the original loop bodies
7243 SmallVector<Value *> condValues;
7244 for (size_t i = 0; i < Loops.size(); ++i) {
7245 BasicBlock *condBlock = BasicBlock::Create(
7246 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7247 Builder.SetInsertPoint(condBlock);
7248 Value *condValue =
7249 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7250 condBBs.push_back(condBlock);
7251 condValues.push_back(condValue);
7252 }
7253 // Join the condition blocks with the bodies of the original loops
7254 redirectTo(fused->getBody(), condBBs[0], DL);
7255 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7256 Builder.SetInsertPoint(condBBs[i]);
7257 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7258 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7259 // Replace the IV with the fused IV
7260 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7261 }
7262 // Last body jumps to the created end body block
7263 Builder.SetInsertPoint(condBBs.back());
7264 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7265 fused->getLatch());
7266 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7267 // Replace the IV with the fused IV
7268 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7269
7270 // The loop latch must have only one predecessor. Currently it is branched to
7271 // from both the last condition block and the last loop body
7272 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7273 "omp.fused.pre_latch");
7274
7275 // Remove unused parts
7276 removeUnusedBlocksFromParent(oldControlBBs);
7277
7278 // Invalidate old CLIs
7279 for (CanonicalLoopInfo *L : Loops)
7280 L->invalidate();
7281
7282#ifndef NDEBUG
7283 fused->assertOK();
7284#endif
7285 return fused;
7286}
7287
7289 LLVMContext &Ctx = Builder.getContext();
7291 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7292 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7293}
7294
7296 LLVMContext &Ctx = Builder.getContext();
7298 Loop, {
7299 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7300 });
7301}
7302
7303void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7304 Value *IfCond, ValueToValueMapTy &VMap,
7305 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7306 const Twine &NamePrefix) {
7307 Function *F = CanonicalLoop->getFunction();
7308
7309 // We can't do
7310 // if (cond) {
7311 // simd_loop;
7312 // } else {
7313 // non_simd_loop;
7314 // }
7315 // because then the CanonicalLoopInfo would only point to one of the loops:
7316 // leading to other constructs operating on the same loop to malfunction.
7317 // Instead generate
7318 // while (...) {
7319 // if (cond) {
7320 // simd_body;
7321 // } else {
7322 // not_simd_body;
7323 // }
7324 // }
7325 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7326 // body at -O3
7327
7328 // Define where if branch should be inserted
7329 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7330
7331 // Create additional blocks for the if statement
7332 BasicBlock *Cond = SplitBeforeIt->getParent();
7333 llvm::LLVMContext &C = Cond->getContext();
7335 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7337 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7338
7339 // Create if condition branch.
7340 Builder.SetInsertPoint(SplitBeforeIt);
7341 Instruction *BrInstr =
7342 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7343 InsertPointTy IP{BrInstr->getParent(), ++BrInstr->getIterator()};
7344 // Then block contains branch to omp loop body which needs to be vectorized
7345 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7346 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7347
7348 Builder.SetInsertPoint(ElseBlock);
7349
7350 // Clone loop for the else branch
7352
7353 SmallVector<BasicBlock *, 8> ExistingBlocks;
7354 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7355 ExistingBlocks.push_back(ThenBlock);
7356 ExistingBlocks.append(L->block_begin(), L->block_end());
7357 // Cond is the block that has the if clause condition
7358 // LoopCond is omp_loop.cond
7359 // LoopHeader is omp_loop.header
7360 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7361 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7362 assert(LoopCond && LoopHeader && "Invalid loop structure");
7363 for (BasicBlock *Block : ExistingBlocks) {
7364 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7365 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7366 continue;
7367 }
7368 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7369
7370 // fix name not to be omp.if.then
7371 if (Block == ThenBlock)
7372 NewBB->setName(NamePrefix + ".if.else");
7373
7374 NewBB->moveBefore(CanonicalLoop->getExit());
7375 VMap[Block] = NewBB;
7376 NewBlocks.push_back(NewBB);
7377 }
7378 remapInstructionsInBlocks(NewBlocks, VMap);
7379 Builder.CreateBr(NewBlocks.front());
7380
7381 // The loop latch must have only one predecessor. Currently it is branched to
7382 // from both the 'then' and 'else' branches.
7383 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7384 NamePrefix + ".pre_latch");
7385
7386 // Ensure that the then block is added to the loop so we add the attributes in
7387 // the next step
7388 L->addBasicBlockToLoop(ThenBlock, LI);
7389}
7390
7391unsigned
7393 const StringMap<bool> &Features) {
7394 if (TargetTriple.isX86()) {
7395 if (Features.lookup("avx512f"))
7396 return 512;
7397 else if (Features.lookup("avx"))
7398 return 256;
7399 return 128;
7400 }
7401 if (TargetTriple.isPPC())
7402 return 128;
7403 if (TargetTriple.isWasm())
7404 return 128;
7405 return 0;
7406}
7407
7409 MapVector<Value *, Value *> AlignedVars,
7410 Value *IfCond, OrderKind Order,
7411 ConstantInt *Simdlen, ConstantInt *Safelen) {
7412 LLVMContext &Ctx = Builder.getContext();
7413
7414 Function *F = CanonicalLoop->getFunction();
7415
7416 // Blocks must have terminators.
7417 // FIXME: Don't run analyses on incomplete/invalid IR.
7419 for (BasicBlock &BB : *F)
7420 if (!BB.hasTerminator())
7421 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7422
7423 // TODO: We should not rely on pass manager. Currently we use pass manager
7424 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7425 // object. We should have a method which returns all blocks between
7426 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7428 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7429 FAM.registerPass([]() { return LoopAnalysis(); });
7430 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7431
7432 LoopAnalysis LIA;
7433 LoopInfo &&LI = LIA.run(*F, FAM);
7434
7435 for (Instruction *I : UIs)
7436 I->eraseFromParent();
7437
7438 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7439 if (AlignedVars.size()) {
7440 InsertPointTy IP = Builder.saveIP();
7441 for (auto &AlignedItem : AlignedVars) {
7442 Value *AlignedPtr = AlignedItem.first;
7443 Value *Alignment = AlignedItem.second;
7444 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7445 Builder.SetInsertPoint(loadInst->getNextNode());
7446 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7447 Alignment);
7448 }
7449 Builder.restoreIP(IP);
7450 }
7451
7452 if (IfCond) {
7453 ValueToValueMapTy VMap;
7454 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7455 }
7456
7458
7459 // Get the basic blocks from the loop in which memref instructions
7460 // can be found.
7461 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7462 // preferably without running any passes.
7463 for (BasicBlock *Block : L->getBlocks()) {
7464 if (Block == CanonicalLoop->getCond() ||
7465 Block == CanonicalLoop->getHeader())
7466 continue;
7467 Reachable.insert(Block);
7468 }
7469
7470 SmallVector<Metadata *> LoopMDList;
7471
7472 // In presence of finite 'safelen', it may be unsafe to mark all
7473 // the memory instructions parallel, because loop-carried
7474 // dependences of 'safelen' iterations are possible.
7475 // If clause order(concurrent) is specified then the memory instructions
7476 // are marked parallel even if 'safelen' is finite.
7477 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7478 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7479
7480 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7481 // versions so we can't add the loop attributes in that case.
7482 if (IfCond) {
7483 // we can still add llvm.loop.parallel_access
7484 addLoopMetadata(CanonicalLoop, LoopMDList);
7485 return;
7486 }
7487
7488 // Use the above access group metadata to create loop level
7489 // metadata, which should be distinct for each loop.
7490 ConstantAsMetadata *BoolConst =
7492 LoopMDList.push_back(MDNode::get(
7493 Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable"), BoolConst}));
7494
7495 if (Simdlen || Safelen) {
7496 // If both simdlen and safelen clauses are specified, the value of the
7497 // simdlen parameter must be less than or equal to the value of the safelen
7498 // parameter. Therefore, use safelen only in the absence of simdlen.
7499 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7500 LoopMDList.push_back(
7501 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7502 ConstantAsMetadata::get(VectorizeWidth)}));
7503 }
7504
7505 addLoopMetadata(CanonicalLoop, LoopMDList);
7506}
7507
7508/// Create the TargetMachine object to query the backend for optimization
7509/// preferences.
7510///
7511/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7512/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7513/// needed for the LLVM pass pipline. We use some default options to avoid
7514/// having to pass too many settings from the frontend that probably do not
7515/// matter.
7516///
7517/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7518/// method. If we are going to use TargetMachine for more purposes, especially
7519/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7520/// might become be worth requiring front-ends to pass on their TargetMachine,
7521/// or at least cache it between methods. Note that while fontends such as Clang
7522/// have just a single main TargetMachine per translation unit, "target-cpu" and
7523/// "target-features" that determine the TargetMachine are per-function and can
7524/// be overrided using __attribute__((target("OPTIONS"))).
7525static std::unique_ptr<TargetMachine>
7527 Module *M = F->getParent();
7528
7529 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7530 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7531 const llvm::Triple &Triple = M->getTargetTriple();
7532
7533 std::string Error;
7535 if (!TheTarget)
7536 return {};
7537
7539 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7540 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7541 /*CodeModel=*/std::nullopt, OptLevel));
7542}
7543
7544/// Heuristically determine the best-performant unroll factor for \p CLI. This
7545/// depends on the target processor. We are re-using the same heuristics as the
7546/// LoopUnrollPass.
7548 Function *F = CLI->getFunction();
7549
7550 // Assume the user requests the most aggressive unrolling, even if the rest of
7551 // the code is optimized using a lower setting.
7553 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7554
7555 // Blocks must have terminators.
7556 // FIXME: Don't run analyses on incomplete/invalid IR.
7558 for (BasicBlock &BB : *F)
7559 if (!BB.hasTerminator())
7560 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7561
7563 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7564 FAM.registerPass([]() { return AssumptionAnalysis(); });
7565 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7566 FAM.registerPass([]() { return LoopAnalysis(); });
7567 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7568 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7569 TargetIRAnalysis TIRA;
7570 if (TM)
7571 TIRA = TargetIRAnalysis(
7572 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7573 FAM.registerPass([&]() { return TIRA; });
7574
7575 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7577 ScalarEvolution &&SE = SEA.run(*F, FAM);
7579 DominatorTree &&DT = DTA.run(*F, FAM);
7580 LoopAnalysis LIA;
7581 LoopInfo &&LI = LIA.run(*F, FAM);
7583 AssumptionCache &&AC = ACT.run(*F, FAM);
7585
7586 for (Instruction *I : UIs)
7587 I->eraseFromParent();
7588
7589 Loop *L = LI.getLoopFor(CLI->getHeader());
7590 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7591
7593 L, SE, TTI,
7594 /*BlockFrequencyInfo=*/nullptr,
7595 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7596 /*UserThreshold=*/std::nullopt,
7597 /*UserAllowPartial=*/true,
7598 /*UserAllowRuntime=*/true,
7599 /*UserUpperBound=*/std::nullopt,
7600 /*UserFullUnrollMaxCount=*/std::nullopt);
7601
7602 UP.Force = true;
7603
7604 // Account for additional optimizations taking place before the LoopUnrollPass
7605 // would unroll the loop.
7608
7609 // Use normal unroll factors even if the rest of the code is optimized for
7610 // size.
7613
7614 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7615 << " Threshold=" << UP.Threshold << "\n"
7616 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7617 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7618 << " PartialOptSizeThreshold="
7619 << UP.PartialOptSizeThreshold << "\n");
7620
7621 // Disable peeling.
7624 /*UserAllowPeeling=*/false,
7625 /*UserAllowProfileBasedPeeling=*/false,
7626 /*UnrollingSpecficValues=*/false);
7627
7629 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7630
7631 // Assume that reads and writes to stack variables can be eliminated by
7632 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7633 // size.
7634 for (BasicBlock *BB : L->blocks()) {
7635 for (Instruction &I : *BB) {
7636 Value *Ptr;
7637 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7638 Ptr = Load->getPointerOperand();
7639 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7640 Ptr = Store->getPointerOperand();
7641 } else
7642 continue;
7643
7644 Ptr = Ptr->stripPointerCasts();
7645
7646 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7647 if (Alloca->getParent() == &F->getEntryBlock())
7648 EphValues.insert(&I);
7649 }
7650 }
7651 }
7652
7653 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7654
7655 // Loop is not unrollable if the loop contains certain instructions.
7656 if (!UCE.canUnroll()) {
7657 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7658 return 1;
7659 }
7660
7661 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7662 << "\n");
7663
7664 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7665 // be able to use it.
7666 int TripCount = 0;
7667 int MaxTripCount = 0;
7668 bool MaxOrZero = false;
7669 unsigned TripMultiple = 0;
7670
7671 unsigned Factor =
7672 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7673 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7674 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7675
7676 // This function returns 1 to signal to not unroll a loop.
7677 if (Factor == 0)
7678 return 1;
7679 return Factor;
7680}
7681
7683 int32_t Factor,
7684 CanonicalLoopInfo **UnrolledCLI) {
7685 assert(Factor >= 0 && "Unroll factor must not be negative");
7686
7687 Function *F = Loop->getFunction();
7688 LLVMContext &Ctx = F->getContext();
7689
7690 // If the unrolled loop is not used for another loop-associated directive, it
7691 // is sufficient to add metadata for the LoopUnrollPass.
7692 if (!UnrolledCLI) {
7693 SmallVector<Metadata *, 2> LoopMetadata;
7694 LoopMetadata.push_back(
7695 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7696
7697 if (Factor >= 1) {
7699 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7700 LoopMetadata.push_back(MDNode::get(
7701 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7702 }
7703
7704 addLoopMetadata(Loop, LoopMetadata);
7705 return;
7706 }
7707
7708 // Heuristically determine the unroll factor.
7709 if (Factor == 0)
7711
7712 // No change required with unroll factor 1.
7713 if (Factor == 1) {
7714 *UnrolledCLI = Loop;
7715 return;
7716 }
7717
7718 assert(Factor >= 2 &&
7719 "unrolling only makes sense with a factor of 2 or larger");
7720
7721 Type *IndVarTy = Loop->getIndVarType();
7722
7723 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7724 // unroll the inner loop.
7725 Value *FactorVal =
7726 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7727 /*isSigned=*/false));
7728 std::vector<CanonicalLoopInfo *> LoopNest =
7729 tileLoops(DL, {Loop}, {FactorVal});
7730 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7731 *UnrolledCLI = LoopNest[0];
7732 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7733
7734 // LoopUnrollPass can only fully unroll loops with constant trip count.
7735 // Unroll by the unroll factor with a fallback epilog for the remainder
7736 // iterations if necessary.
7738 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7740 InnerLoop,
7741 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7743 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
7744
7745#ifndef NDEBUG
7746 (*UnrolledCLI)->assertOK();
7747#endif
7748}
7749
7752 llvm::Value *BufSize, llvm::Value *CpyBuf,
7753 llvm::Value *CpyFn, llvm::Value *DidIt) {
7754 if (!updateToLocation(Loc))
7755 return Loc.IP;
7756
7757 uint32_t SrcLocStrSize;
7758 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7759 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7760 Value *ThreadId = getOrCreateThreadID(Ident);
7761
7762 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
7763
7764 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
7765
7766 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
7767 createRuntimeFunctionCall(Fn, Args);
7768
7769 return Builder.saveIP();
7770}
7771
7773 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7774 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
7776
7777 if (!updateToLocation(Loc))
7778 return Loc.IP;
7779
7780 // If needed allocate and initialize `DidIt` with 0.
7781 // DidIt: flag variable: 1=single thread; 0=not single thread.
7782 llvm::Value *DidIt = nullptr;
7783 if (!CPVars.empty()) {
7784 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
7785 Builder.CreateStore(Builder.getInt32(0), DidIt);
7786 }
7787
7788 Directive OMPD = Directive::OMPD_single;
7789 uint32_t SrcLocStrSize;
7790 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7791 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7792 Value *ThreadId = getOrCreateThreadID(Ident);
7793 Value *Args[] = {Ident, ThreadId};
7794
7795 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
7796 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
7797
7798 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
7799 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
7800
7801 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
7802 if (Error Err = FiniCB(IP))
7803 return Err;
7804
7805 // The thread that executes the single region must set `DidIt` to 1.
7806 // This is used by __kmpc_copyprivate, to know if the caller is the
7807 // single thread or not.
7808 if (DidIt)
7809 Builder.CreateStore(Builder.getInt32(1), DidIt);
7810
7811 return Error::success();
7812 };
7813
7814 // generates the following:
7815 // if (__kmpc_single()) {
7816 // .... single region ...
7817 // __kmpc_end_single
7818 // }
7819 // __kmpc_copyprivate
7820 // __kmpc_barrier
7821
7822 InsertPointOrErrorTy AfterIP =
7823 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
7824 /*Conditional*/ true,
7825 /*hasFinalize*/ true);
7826 if (!AfterIP)
7827 return AfterIP.takeError();
7828
7829 if (DidIt) {
7830 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
7831 // NOTE BufSize is currently unused, so just pass 0.
7833 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
7834 CPFuncs[I], DidIt);
7835 // NOTE __kmpc_copyprivate already inserts a barrier
7836 } else if (!IsNowait) {
7837 InsertPointOrErrorTy AfterIP =
7839 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
7840 /* CheckCancelFlag */ false);
7841 if (!AfterIP)
7842 return AfterIP.takeError();
7843 }
7844 return Builder.saveIP();
7845}
7846
7849 BodyGenCallbackTy BodyGenCB,
7850 FinalizeCallbackTy FiniCB, bool IsNowait) {
7851
7852 if (!updateToLocation(Loc))
7853 return Loc.IP;
7854
7855 // All threads execute the scope body — no conditional entry.
7856 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
7857 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
7858 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
7859 /*IsCancellable=*/false);
7860 if (!AfterIP)
7861 return AfterIP.takeError();
7862
7863 Builder.restoreIP(*AfterIP);
7864 if (!IsNowait) {
7865 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
7866 omp::Directive::OMPD_unknown,
7867 /*ForceSimpleCall=*/false,
7868 /*CheckCancelFlag=*/false);
7869 if (!AfterIP)
7870 return AfterIP.takeError();
7871 }
7872 return Builder.saveIP();
7873}
7874
7876 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7877 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
7878
7879 if (!updateToLocation(Loc))
7880 return Loc.IP;
7881
7882 Directive OMPD = Directive::OMPD_critical;
7883 uint32_t SrcLocStrSize;
7884 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7885 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7886 Value *ThreadId = getOrCreateThreadID(Ident);
7887 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
7888 Value *Args[] = {Ident, ThreadId, LockVar};
7889
7890 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
7891 Function *RTFn = nullptr;
7892 if (HintInst) {
7893 // Add Hint to entry Args and create call
7894 EnterArgs.push_back(HintInst);
7895 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
7896 } else {
7897 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
7898 }
7899 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
7900
7901 Function *ExitRTLFn =
7902 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
7903 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
7904
7905 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
7906 /*Conditional*/ false, /*hasFinalize*/ true);
7907}
7908
7911 InsertPointTy AllocaIP, unsigned NumLoops,
7912 ArrayRef<llvm::Value *> StoreValues,
7913 const Twine &Name, bool IsDependSource) {
7914 assert(
7915 llvm::all_of(StoreValues,
7916 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
7917 "OpenMP runtime requires depend vec with i64 type");
7918
7919 if (!updateToLocation(Loc))
7920 return Loc.IP;
7921
7922 // Allocate space for vector and generate alloc instruction.
7923 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
7924 Builder.restoreIP(AllocaIP);
7925 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
7926 ArgsBase->setAlignment(Align(8));
7928
7929 // Store the index value with offset in depend vector.
7930 for (unsigned I = 0; I < NumLoops; ++I) {
7931 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
7932 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
7933 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
7934 STInst->setAlignment(Align(8));
7935 }
7936
7937 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
7938 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
7939
7940 uint32_t SrcLocStrSize;
7941 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7942 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7943 Value *ThreadId = getOrCreateThreadID(Ident);
7944 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
7945
7946 Function *RTLFn = nullptr;
7947 if (IsDependSource)
7948 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
7949 else
7950 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
7951 createRuntimeFunctionCall(RTLFn, Args);
7952
7953 return Builder.saveIP();
7954}
7955
7957 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7958 FinalizeCallbackTy FiniCB, bool IsThreads) {
7959 if (!updateToLocation(Loc))
7960 return Loc.IP;
7961
7962 Directive OMPD = Directive::OMPD_ordered;
7963 Instruction *EntryCall = nullptr;
7964 Instruction *ExitCall = nullptr;
7965
7966 if (IsThreads) {
7967 uint32_t SrcLocStrSize;
7968 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7969 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7970 Value *ThreadId = getOrCreateThreadID(Ident);
7971 Value *Args[] = {Ident, ThreadId};
7972
7973 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
7974 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
7975
7976 Function *ExitRTLFn =
7977 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
7978 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
7979 }
7980
7981 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
7982 /*Conditional*/ false, /*hasFinalize*/ true);
7983}
7984
7985OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
7986 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
7987 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
7988 bool HasFinalize, bool IsCancellable) {
7989
7990 if (HasFinalize)
7991 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
7992
7993 // Create inlined region's entry and body blocks, in preparation
7994 // for conditional creation
7995 BasicBlock *EntryBB = Builder.GetInsertBlock();
7996 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
7998 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
7999 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8000 BasicBlock *FiniBB =
8001 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8002
8003 Builder.SetInsertPoint(EntryBB->getTerminator());
8004 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8005
8006 // generate body
8007 if (Error Err =
8008 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8009 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8010 return Err;
8011
8012 // emit exit call and do any needed finalization.
8013 auto FinIP = InsertPointTy(FiniBB, FiniBB->getFirstInsertionPt());
8014 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8015 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8016 "Unexpected control flow graph state!!");
8017 InsertPointOrErrorTy AfterIP =
8018 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8019 if (!AfterIP)
8020 return AfterIP.takeError();
8021
8022 // If we are skipping the region of a non conditional, remove the exit
8023 // block, and clear the builder's insertion point.
8024 assert(SplitPos->getParent() == ExitBB &&
8025 "Unexpected Insertion point location!");
8026 auto merged = MergeBlockIntoPredecessor(ExitBB);
8027 BasicBlock *ExitPredBB = SplitPos->getParent();
8028 auto InsertBB = merged ? ExitPredBB : ExitBB;
8030 SplitPos->eraseFromParent();
8031 Builder.SetInsertPoint(InsertBB);
8032
8033 return Builder.saveIP();
8034}
8035
8036OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8037 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8038 // if nothing to do, Return current insertion point.
8039 if (!Conditional || !EntryCall)
8040 return Builder.saveIP();
8041
8042 BasicBlock *EntryBB = Builder.GetInsertBlock();
8043 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8044 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8045 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8046
8047 // Emit thenBB and set the Builder's insertion point there for
8048 // body generation next. Place the block after the current block.
8049 Function *CurFn = EntryBB->getParent();
8050 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8051
8052 // Move Entry branch to end of ThenBB, and replace with conditional
8053 // branch (If-stmt)
8054 Instruction *EntryBBTI = EntryBB->getTerminator();
8055 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8056 EntryBBTI->removeFromParent();
8057 Builder.SetInsertPoint(UI);
8058 Builder.Insert(EntryBBTI);
8059 UI->eraseFromParent();
8060 Builder.SetInsertPoint(ThenBB->getTerminator());
8061
8062 // return an insertion point to ExitBB.
8063 return IRBuilder<>::InsertPoint(ExitBB, ExitBB->getFirstInsertionPt());
8064}
8065
8066OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8067 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8068 bool HasFinalize) {
8069
8070 Builder.restoreIP(FinIP);
8071
8072 // If there is finalization to do, emit it before the exit call
8073 if (HasFinalize) {
8074 assert(!FinalizationStack.empty() &&
8075 "Unexpected finalization stack state!");
8076
8077 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8078 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8079
8080 if (Error Err = Fi.mergeFiniBB(Builder, FinIP.getBlock()))
8081 return std::move(Err);
8082
8083 // Exit condition: insertion point is before the terminator of the new Fini
8084 // block
8085 Builder.SetInsertPoint(FinIP.getBlock()->getTerminator());
8086 }
8087
8088 if (!ExitCall)
8089 return Builder.saveIP();
8090
8091 // place the Exitcall as last instruction before Finalization block terminator
8092 ExitCall->removeFromParent();
8093 Builder.Insert(ExitCall);
8094
8095 return IRBuilder<>::InsertPoint(ExitCall->getParent(),
8096 ExitCall->getIterator());
8097}
8098
8100 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8101 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8102 if (!IP.isSet())
8103 return IP;
8104
8106
8107 // creates the following CFG structure
8108 // OMP_Entry : (MasterAddr != PrivateAddr)?
8109 // F T
8110 // | \
8111 // | copin.not.master
8112 // | /
8113 // v /
8114 // copyin.not.master.end
8115 // |
8116 // v
8117 // OMP.Entry.Next
8118
8119 BasicBlock *OMP_Entry = IP.getBlock();
8120 Function *CurFn = OMP_Entry->getParent();
8121 BasicBlock *CopyBegin =
8122 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8123 BasicBlock *CopyEnd = nullptr;
8124
8125 // If entry block is terminated, split to preserve the branch to following
8126 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8128 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8129 "copyin.not.master.end");
8130 OMP_Entry->getTerminator()->eraseFromParent();
8131 } else {
8132 CopyEnd =
8133 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8134 }
8135
8136 Builder.SetInsertPoint(OMP_Entry);
8137 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8138 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8139 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8140 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8141
8142 Builder.SetInsertPoint(CopyBegin);
8143 if (BranchtoEnd)
8144 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8145
8146 return Builder.saveIP();
8147}
8148
8150 Value *Size, Value *Allocator,
8151 std::string Name) {
8153 if (!updateToLocation(Loc))
8154 return nullptr;
8155
8156 uint32_t SrcLocStrSize;
8157 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8158 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8159 Value *ThreadId = getOrCreateThreadID(Ident);
8160 Value *Args[] = {ThreadId, Size, Allocator};
8161
8162 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8163
8164 return createRuntimeFunctionCall(Fn, Args, Name);
8165}
8166
8168 Value *Align, Value *Size,
8169 Value *Allocator,
8170 std::string Name) {
8172 if (!updateToLocation(Loc))
8173 return nullptr;
8174
8175 uint32_t SrcLocStrSize;
8176 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8177 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8178 Value *ThreadId = getOrCreateThreadID(Ident);
8179 Value *Args[] = {ThreadId, Align, Size, Allocator};
8180
8181 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8182
8183 return Builder.CreateCall(Fn, Args, Name);
8184}
8185
8187 Value *Addr, Value *Allocator,
8188 std::string Name) {
8190 if (!updateToLocation(Loc))
8191 return nullptr;
8192
8193 uint32_t SrcLocStrSize;
8194 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8195 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8196 Value *ThreadId = getOrCreateThreadID(Ident);
8197 Value *Args[] = {ThreadId, Addr, Allocator};
8198 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8199 return createRuntimeFunctionCall(Fn, Args, Name);
8200}
8201
8203 Value *Size,
8204 const Twine &Name) {
8207
8208 Value *Args[] = {Size};
8209 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8210 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8212 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8213 return Call;
8214}
8215
8217 Type *VarType,
8218 const Twine &Name) {
8219 return createOMPAllocShared(
8220 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8221}
8222
8224 Value *Addr, Value *Size,
8225 const Twine &Name) {
8228
8229 Value *Args[] = {Addr, Size};
8230 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8231 return Builder.CreateCall(Fn, Args, Name);
8232}
8233
8235 Value *Addr, Type *VarType,
8236 const Twine &Name) {
8237 return createOMPFreeShared(
8238 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8239 Name);
8240}
8241
8243 const LocationDescription &Loc, Value *InteropVar,
8244 omp::OMPInteropType InteropType, Value *Device, Value *NumDependences,
8245 Value *DependenceAddress, bool HaveNowaitClause) {
8248
8249 uint32_t SrcLocStrSize;
8250 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8251 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8252 Value *ThreadId = getOrCreateThreadID(Ident);
8253 if (Device == nullptr)
8254 Device = Constant::getAllOnesValue(Int32);
8255 else if (Device->getType() != Int32)
8256 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8257 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8258 if (NumDependences == nullptr) {
8259 NumDependences = ConstantInt::get(Int32, 0);
8260 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8261 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8262 }
8263 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8264 Value *Args[] = {
8265 Ident, ThreadId, InteropVar, InteropTypeVal,
8266 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8267
8268 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8269
8270 return createRuntimeFunctionCall(Fn, Args);
8271}
8272
8274 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8275 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8278
8279 uint32_t SrcLocStrSize;
8280 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8281 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8282 Value *ThreadId = getOrCreateThreadID(Ident);
8283 if (Device == nullptr)
8284 Device = Constant::getAllOnesValue(Int32);
8285 else if (Device->getType() != Int32)
8286 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8287 if (NumDependences == nullptr) {
8288 NumDependences = ConstantInt::get(Int32, 0);
8289 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8290 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8291 }
8292 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8293 Value *Args[] = {
8294 Ident, ThreadId, InteropVar, Device,
8295 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8296
8297 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8298
8299 return createRuntimeFunctionCall(Fn, Args);
8300}
8301
8303 Value *InteropVar, Value *Device,
8304 Value *NumDependences,
8305 Value *DependenceAddress,
8306 bool HaveNowaitClause) {
8309 uint32_t SrcLocStrSize;
8310 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8311 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8312 Value *ThreadId = getOrCreateThreadID(Ident);
8313 if (Device == nullptr)
8314 Device = Constant::getAllOnesValue(Int32);
8315 else if (Device->getType() != Int32)
8316 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8317 if (NumDependences == nullptr) {
8318 NumDependences = ConstantInt::get(Int32, 0);
8319 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8320 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8321 }
8322 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8323 Value *Args[] = {
8324 Ident, ThreadId, InteropVar, Device,
8325 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8326
8327 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8328
8329 return createRuntimeFunctionCall(Fn, Args);
8330}
8331
8334 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8337
8338 uint32_t SrcLocStrSize;
8339 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8340 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8341 Value *ThreadId = getOrCreateThreadID(Ident);
8342 Constant *ThreadPrivateCache =
8343 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8344 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8345
8346 Function *Fn =
8347 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8348
8349 return createRuntimeFunctionCall(Fn, Args);
8350}
8351
8353 const LocationDescription &Loc,
8355 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8356 "expected num_threads and num_teams to be specified");
8357
8358 if (!updateToLocation(Loc))
8359 return Loc.IP;
8360
8361 uint32_t SrcLocStrSize;
8362 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8363 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8364 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8365 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8366 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8367 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8368 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8369 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8370
8371 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8372 Function *Kernel = DebugKernelWrapper;
8373
8374 // We need to strip the debug prefix to get the correct kernel name.
8375 StringRef KernelName = Kernel->getName();
8376 const std::string DebugPrefix = "_debug__";
8377 if (KernelName.ends_with(DebugPrefix)) {
8378 KernelName = KernelName.drop_back(DebugPrefix.length());
8379 Kernel = M.getFunction(KernelName);
8380 assert(Kernel && "Expected the real kernel to exist");
8381 }
8382
8383 // Manifest the launch configuration in the metadata matching the kernel
8384 // environment.
8385 if (Attrs.MinTeams > 1 || Attrs.MaxTeams.front() > 0)
8386 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams, Attrs.MaxTeams.front());
8387
8388 // If MaxThreads is not set and needs adjustment, select the maximum between
8389 // the default workgroup size and the MinThreads value.
8390 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8391 if (MaxThreadsVal < 0 && UseDefaultMaxThreads) {
8392 if (hasGridValue(T)) {
8393 MaxThreadsVal =
8394 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8395 Attrs.MinThreads);
8396 } else {
8397 MaxThreadsVal = Attrs.MinThreads;
8398 }
8399 }
8400
8401 if (MaxThreadsVal > 0)
8402 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads, MaxThreadsVal);
8403
8404 Constant *MinThreads = ConstantInt::getSigned(Int32, Attrs.MinThreads);
8405 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8406 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams);
8407 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8408 Constant *ReductionDataSize =
8409 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8410
8412 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8413 const DataLayout &DL = Fn->getDataLayout();
8414
8415 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8416 Constant *DynamicEnvironmentInitializer =
8417 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8418 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8419 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8420 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8421 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8422 DL.getDefaultGlobalsAddressSpace());
8423 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8424
8425 Constant *DynamicEnvironment =
8426 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8427 ? DynamicEnvironmentGV
8428 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8429 DynamicEnvironmentPtr);
8430
8431 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8432 ConfigurationEnvironment, {
8433 UseGenericStateMachineVal,
8434 MayUseNestedParallelismVal,
8435 IsSPMDVal,
8436 MinThreads,
8437 MaxThreads,
8438 MinTeams,
8439 MaxTeams,
8440 ReductionDataSize,
8441 });
8442 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8443 KernelEnvironment, {
8444 ConfigurationEnvironmentInitializer,
8445 Ident,
8446 DynamicEnvironment,
8447 });
8448 std::string KernelEnvironmentName =
8449 (KernelName + "_kernel_environment").str();
8450 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8451 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8452 KernelEnvironmentInitializer, KernelEnvironmentName,
8453 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8454 DL.getDefaultGlobalsAddressSpace());
8455 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8456
8457 Constant *KernelEnvironment =
8458 KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8459 ? KernelEnvironmentGV
8460 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8461 KernelEnvironmentPtr);
8462 Value *KernelLaunchEnvironment =
8463 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8464 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8465 KernelLaunchEnvironment =
8466 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8467 ? KernelLaunchEnvironment
8468 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8469 KernelLaunchEnvParamTy);
8470 CallInst *ThreadKind = createRuntimeFunctionCall(
8471 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8472
8473 Value *ExecUserCode = Builder.CreateICmpEQ(
8474 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8475 "exec_user_code");
8476
8477 // ThreadKind = __kmpc_target_init(...)
8478 // if (ThreadKind == -1)
8479 // user_code
8480 // else
8481 // return;
8482
8483 auto *UI = Builder.CreateUnreachable();
8484 BasicBlock *CheckBB = UI->getParent();
8485 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8486
8487 BasicBlock *WorkerExitBB = BasicBlock::Create(
8488 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8489 Builder.SetInsertPoint(WorkerExitBB);
8490 Builder.CreateRetVoid();
8491
8492 auto *CheckBBTI = CheckBB->getTerminator();
8493 Builder.SetInsertPoint(CheckBBTI);
8494 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8495
8496 CheckBBTI->eraseFromParent();
8497 UI->eraseFromParent();
8498
8499 // Continue in the "user_code" block, see diagram above and in
8500 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8501 return InsertPointTy(UserCodeEntryBB, UserCodeEntryBB->getFirstInsertionPt());
8502}
8503
8505 int32_t TeamsReductionDataSize) {
8506 if (!updateToLocation(Loc))
8507 return;
8508
8510 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8511
8513
8514 if (!TeamsReductionDataSize)
8515 return;
8516
8517 Function *Kernel = Builder.GetInsertBlock()->getParent();
8518 // We need to strip the debug prefix to get the correct kernel name.
8519 StringRef KernelName = Kernel->getName();
8520 const std::string DebugPrefix = "_debug__";
8521 if (KernelName.ends_with(DebugPrefix))
8522 KernelName = KernelName.drop_back(DebugPrefix.length());
8523 auto *KernelEnvironmentGV =
8524 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8525 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8526 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8527 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8528 KernelEnvironmentInitializer,
8529 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8530 KernelEnvironmentGV->setInitializer(NewInitializer);
8531}
8532
8533static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8534 bool Min) {
8535 if (Kernel.hasFnAttribute(Name)) {
8536 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8537 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8538 }
8539 Kernel.addFnAttr(Name, llvm::utostr(Value));
8540}
8541
8542std::pair<int32_t, int32_t>
8544 int32_t ThreadLimit =
8545 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8546
8547 if (T.isAMDGPU()) {
8548 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8549 if (!Attr.isValid() || !Attr.isStringAttribute())
8550 return {0, ThreadLimit};
8551 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8552 int32_t LB, UB;
8553 if (!llvm::to_integer(UBStr, UB, 10))
8554 return {0, ThreadLimit};
8555 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8556 if (!llvm::to_integer(LBStr, LB, 10))
8557 return {0, UB};
8558 return {LB, UB};
8559 }
8560
8561 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8562 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8563 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8564 }
8565 return {0, ThreadLimit};
8566}
8567
8569 Function &Kernel, int32_t LB,
8570 int32_t UB) {
8571 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8572
8573 if (T.isAMDGPU()) {
8574 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8575 llvm::utostr(LB) + "," + llvm::utostr(UB));
8576 return;
8577 }
8578
8580}
8581
8582std::pair<int32_t, int32_t>
8584 // TODO: Read from backend annotations if available.
8585 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8586}
8587
8589 int32_t LB, int32_t UB) {
8590 if (UB > 0) {
8591 if (T.isNVPTX())
8593 if (T.isAMDGPU())
8594 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8595 }
8596
8597 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8598}
8599
8600void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8601 Function *OutlinedFn) {
8602 if (Config.isTargetDevice()) {
8604 // TODO: Determine if DSO local can be set to true.
8605 OutlinedFn->setDSOLocal(false);
8607 if (T.isAMDGCN())
8609 else if (T.isNVPTX())
8611 else if (T.isSPIRV())
8613 }
8614}
8615
8616Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8617 StringRef EntryFnIDName) {
8618 if (Config.isTargetDevice()) {
8619 assert(OutlinedFn && "The outlined function must exist if embedded");
8620 return OutlinedFn;
8621 }
8622
8623 return new GlobalVariable(
8624 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8625 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8626}
8627
8628Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8629 StringRef EntryFnName) {
8630 if (OutlinedFn)
8631 return OutlinedFn;
8632
8633 assert(!M.getGlobalVariable(EntryFnName, true) &&
8634 "Named kernel already exists?");
8635 return new GlobalVariable(
8636 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8637 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8638}
8639
8641 TargetRegionEntryInfo &EntryInfo,
8642 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8643 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8644
8645 SmallString<64> EntryFnName;
8646 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8647
8648 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8649 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8650 if (!CBResult)
8651 return CBResult.takeError();
8652 OutlinedFn = *CBResult;
8653 } else {
8654 OutlinedFn = nullptr;
8655 }
8656
8657 // If this target outline function is not an offload entry, we don't need to
8658 // register it. This may be in the case of a false if clause, or if there are
8659 // no OpenMP targets.
8660 if (!IsOffloadEntry)
8661 return Error::success();
8662
8663 std::string EntryFnIDName =
8664 Config.isTargetDevice()
8665 ? std::string(EntryFnName)
8666 : createPlatformSpecificName({EntryFnName, "region_id"});
8667
8668 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8669 EntryFnName, EntryFnIDName);
8670 return Error::success();
8671}
8672
8674 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8675 StringRef EntryFnName, StringRef EntryFnIDName) {
8676 if (OutlinedFn)
8677 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8678 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8679 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8680 OffloadInfoManager.registerTargetRegionEntryInfo(
8681 EntryInfo, EntryAddr, OutlinedFnID,
8683 return OutlinedFnID;
8684}
8685
8687 const LocationDescription &Loc, InsertPointTy AllocaIP,
8688 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8689 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8690 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8691 omp::RuntimeFunction *MapperFunc,
8693 BodyGenTy BodyGenType)>
8694 BodyGenCB,
8695 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8696 if (!updateToLocation(Loc))
8697 return InsertPointTy();
8698
8699 Builder.restoreIP(CodeGenIP);
8700
8701 bool IsStandAlone = !BodyGenCB;
8702 MapInfosTy *MapInfo;
8703 // Generate the code for the opening of the data environment. Capture all the
8704 // arguments of the runtime call by reference because they are used in the
8705 // closing of the region.
8706 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8707 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8708 MapInfo = &GenMapInfoCB(Builder.saveIP());
8709 if (Error Err = emitOffloadingArrays(
8710 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
8711 /*IsNonContiguous=*/true, DeviceAddrCB))
8712 return Err;
8713
8714 TargetDataRTArgs RTArgs;
8716
8717 // Emit the number of elements in the offloading arrays.
8718 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8719
8720 // Source location for the ident struct
8721 if (!SrcLocInfo) {
8722 uint32_t SrcLocStrSize;
8723 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8724 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8725 }
8726
8727 SmallVector<llvm::Value *, 13> OffloadingArgs = {
8728 SrcLocInfo, DeviceID,
8729 PointerNum, RTArgs.BasePointersArray,
8730 RTArgs.PointersArray, RTArgs.SizesArray,
8731 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8732 RTArgs.MappersArray};
8733
8734 if (IsStandAlone) {
8735 assert(MapperFunc && "MapperFunc missing for standalone target data");
8736
8737 auto TaskBodyCB = [&](Value *, Value *,
8739 if (Info.HasNoWait) {
8740 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
8744 }
8745
8747 OffloadingArgs);
8748
8749 if (Info.HasNoWait) {
8750 BasicBlock *OffloadContBlock =
8751 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
8752 Function *CurFn = Builder.GetInsertBlock()->getParent();
8753 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
8754 Builder.restoreIP(Builder.saveIP());
8755 }
8756 return Error::success();
8757 };
8758
8759 bool RequiresOuterTargetTask = Info.HasNoWait;
8760 if (!RequiresOuterTargetTask)
8761 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
8762 /*TargetTaskAllocaIP=*/{}));
8763 else
8764 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
8765 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
8766 } else {
8767 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
8768 omp::OMPRTL___tgt_target_data_begin_mapper);
8769
8770 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
8771
8772 for (auto DeviceMap : Info.DevicePtrInfoMap) {
8773 if (isa<AllocaInst>(DeviceMap.second.second)) {
8774 auto *LI =
8775 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
8776 Builder.CreateStore(LI, DeviceMap.second.second);
8777 }
8778 }
8779
8780 // If device pointer privatization is required, emit the body of the
8781 // region here. It will have to be duplicated: with and without
8782 // privatization.
8783 InsertPointOrErrorTy AfterIP =
8784 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
8785 if (!AfterIP)
8786 return AfterIP.takeError();
8787 Builder.restoreIP(*AfterIP);
8788 }
8789 return Error::success();
8790 };
8791
8792 // If we need device pointer privatization, we need to emit the body of the
8793 // region with no privatization in the 'else' branch of the conditional.
8794 // Otherwise, we don't have to do anything.
8795 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8796 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8797 InsertPointOrErrorTy AfterIP =
8798 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
8799 if (!AfterIP)
8800 return AfterIP.takeError();
8801 Builder.restoreIP(*AfterIP);
8802 return Error::success();
8803 };
8804
8805 // Generate code for the closing of the data region.
8806 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8807 ArrayRef<BasicBlock *> DeallocBlocks) {
8808 TargetDataRTArgs RTArgs;
8809 Info.EmitDebug = !MapInfo->Names.empty();
8810 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
8811
8812 // Emit the number of elements in the offloading arrays.
8813 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8814
8815 // Source location for the ident struct
8816 if (!SrcLocInfo) {
8817 uint32_t SrcLocStrSize;
8818 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8819 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8820 }
8821
8822 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
8823 PointerNum, RTArgs.BasePointersArray,
8824 RTArgs.PointersArray, RTArgs.SizesArray,
8825 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8826 RTArgs.MappersArray};
8827 Function *EndMapperFunc =
8828 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
8829
8830 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
8831 return Error::success();
8832 };
8833
8834 // We don't have to do anything to close the region if the if clause evaluates
8835 // to false.
8836 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8837 ArrayRef<BasicBlock *> DeallocBlocks) {
8838 return Error::success();
8839 };
8840
8841 Error Err = [&]() -> Error {
8842 if (BodyGenCB) {
8843 Error Err = [&]() {
8844 if (IfCond)
8845 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
8846 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
8847 }();
8848
8849 if (Err)
8850 return Err;
8851
8852 // If we don't require privatization of device pointers, we emit the body
8853 // in between the runtime calls. This avoids duplicating the body code.
8854 InsertPointOrErrorTy AfterIP =
8855 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
8856 if (!AfterIP)
8857 return AfterIP.takeError();
8858 restoreIPandDebugLoc(Builder, *AfterIP);
8859
8860 if (IfCond)
8861 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
8862 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
8863 }
8864 if (IfCond)
8865 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
8866 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
8867 }();
8868
8869 if (Err)
8870 return Err;
8871
8872 return Builder.saveIP();
8873}
8874
8877 bool IsGPUDistribute) {
8878 assert((IVSize == 32 || IVSize == 64) &&
8879 "IV size is not compatible with the omp runtime");
8880 RuntimeFunction Name;
8881 if (IsGPUDistribute)
8882 Name = IVSize == 32
8883 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
8884 : omp::OMPRTL___kmpc_distribute_static_init_4u)
8885 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
8886 : omp::OMPRTL___kmpc_distribute_static_init_8u);
8887 else
8888 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
8889 : omp::OMPRTL___kmpc_for_static_init_4u)
8890 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
8891 : omp::OMPRTL___kmpc_for_static_init_8u);
8892
8893 return getOrCreateRuntimeFunction(M, Name);
8894}
8895
8897 bool IVSigned) {
8898 assert((IVSize == 32 || IVSize == 64) &&
8899 "IV size is not compatible with the omp runtime");
8900 RuntimeFunction Name = IVSize == 32
8901 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
8902 : omp::OMPRTL___kmpc_dispatch_init_4u)
8903 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
8904 : omp::OMPRTL___kmpc_dispatch_init_8u);
8905
8906 return getOrCreateRuntimeFunction(M, Name);
8907}
8908
8910 bool IVSigned) {
8911 assert((IVSize == 32 || IVSize == 64) &&
8912 "IV size is not compatible with the omp runtime");
8913 RuntimeFunction Name = IVSize == 32
8914 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
8915 : omp::OMPRTL___kmpc_dispatch_next_4u)
8916 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
8917 : omp::OMPRTL___kmpc_dispatch_next_8u);
8918
8919 return getOrCreateRuntimeFunction(M, Name);
8920}
8921
8923 bool IVSigned) {
8924 assert((IVSize == 32 || IVSize == 64) &&
8925 "IV size is not compatible with the omp runtime");
8926 RuntimeFunction Name = IVSize == 32
8927 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
8928 : omp::OMPRTL___kmpc_dispatch_fini_4u)
8929 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
8930 : omp::OMPRTL___kmpc_dispatch_fini_8u);
8931
8932 return getOrCreateRuntimeFunction(M, Name);
8933}
8934
8936 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
8937}
8938
8940 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
8941 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
8942
8943 DISubprogram *NewSP = Func->getSubprogram();
8944 if (!NewSP)
8945 return;
8946
8948
8949 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
8950 DILocalVariable *&NewVar = RemappedVariables[OldVar];
8951 // Only use cached variable if the arg number matches. This is important
8952 // so that DIVariable created for privatized variables are not discarded.
8953 if (NewVar && (arg == NewVar->getArg()))
8954 return NewVar;
8955
8957 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
8958 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
8959 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
8960 return NewVar;
8961 };
8962
8963 auto UpdateDebugRecord = [&](auto *DR) {
8964 DILocalVariable *OldVar = DR->getVariable();
8965 unsigned ArgNo = 0;
8966 for (auto Loc : DR->location_ops()) {
8967 auto Iter = ValueReplacementMap.find(Loc);
8968 if (Iter != ValueReplacementMap.end()) {
8969 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
8970 ArgNo = std::get<1>(Iter->second) + 1;
8971 }
8972 }
8973 if (ArgNo != 0)
8974 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
8975 };
8976
8978 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
8979 if (DVR->getNumVariableLocationOps() != 1u) {
8980 DVR->setKillLocation();
8981 return;
8982 }
8983 Value *Loc = DVR->getVariableLocationOp(0u);
8984 BasicBlock *CurBB = DVR->getParent();
8985 BasicBlock *RequiredBB = nullptr;
8986
8987 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
8988 RequiredBB = LocInst->getParent();
8989 else if (isa<llvm::Argument>(Loc))
8990 RequiredBB = &DVR->getFunction()->getEntryBlock();
8991
8992 if (RequiredBB && RequiredBB != CurBB) {
8993 assert(!RequiredBB->empty());
8994 RequiredBB->insertDbgRecordBefore(DVR->clone(),
8995 RequiredBB->back().getIterator());
8996 DVRsToDelete.push_back(DVR);
8997 }
8998 };
8999
9000 // The location and scope of variable intrinsics and records still point to
9001 // the parent function of the target region. Update them.
9002 for (Instruction &I : instructions(Func)) {
9004 "Unexpected debug intrinsic");
9005 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9006 UpdateDebugRecord(&DVR);
9007 MoveDebugRecordToCorrectBlock(&DVR);
9008 }
9009 }
9010 for (auto *DVR : DVRsToDelete)
9011 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9012 // An extra argument is passed to the device. Create the debug data for it.
9013 if (OMPBuilder.Config.isTargetDevice()) {
9014 DICompileUnit *CU = NewSP->getUnit();
9015 Module *M = Func->getParent();
9016 DIBuilder DB(*M, true, CU);
9017 DIType *VoidPtrTy =
9018 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9019 unsigned ArgNo = Func->arg_size();
9020 DILocalVariable *Var = DB.createParameterVariable(
9021 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9022 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9023 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9024 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9025 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9026 &(*Func->begin()));
9027 }
9028}
9029
9031 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9032 return cast<Operator>(V)->getOperand(0);
9033 return V;
9034}
9035
9037 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9039 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9042 SmallVector<Type *> ParameterTypes;
9043 if (OMPBuilder.Config.isTargetDevice()) {
9044 // All parameters to target devices are passed as pointers
9045 // or i64. This assumes 64-bit address spaces/pointers.
9046 for (auto &Arg : Inputs)
9047 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9048 ? Arg->getType()
9049 : Type::getInt64Ty(Builder.getContext()));
9050 } else {
9051 for (auto &Arg : Inputs)
9052 ParameterTypes.push_back(Arg->getType());
9053 }
9054
9055 // The implicit dyn_ptr argument is always the last parameter on both host
9056 // and device so the argument counts match without runtime manipulation.
9057 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9058 ParameterTypes.push_back(PtrTy);
9059
9060 auto BB = Builder.GetInsertBlock();
9061 auto M = BB->getModule();
9062 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9063 /*isVarArg*/ false);
9064 auto Func =
9065 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9066
9067 // Forward target-cpu and target-features function attributes from the
9068 // original function to the new outlined function.
9069 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9070
9071 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9072 if (TargetCpuAttr.isStringAttribute())
9073 Func->addFnAttr(TargetCpuAttr);
9074
9075 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9076 if (TargetFeaturesAttr.isStringAttribute())
9077 Func->addFnAttr(TargetFeaturesAttr);
9078
9079 if (OMPBuilder.Config.isTargetDevice()) {
9080 Value *ExecMode =
9081 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9082 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9083 }
9084
9085 // Save insert point.
9086 IRBuilder<>::InsertPointGuard IPG(Builder);
9087 // We will generate the entries in the outlined function but the debug
9088 // location may still be pointing to the parent function. Reset it now.
9089 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9090
9091 // Generate the region into the function.
9092 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9093 Builder.SetInsertPoint(EntryBB);
9094
9095 // Insert target init call in the device compilation pass.
9096 if (OMPBuilder.Config.isTargetDevice())
9097 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9098
9099 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9100
9101 // As we embed the user code in the middle of our target region after we
9102 // generate entry code, we must move what allocas we can into the entry
9103 // block to avoid possible breaking optimisations for device
9104 if (OMPBuilder.Config.isTargetDevice())
9106
9107 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9108 BasicBlock *OutlinedBodyBB =
9109 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9111 Builder.saveIP(),
9112 OpenMPIRBuilder::InsertPointTy(OutlinedBodyBB, OutlinedBodyBB->begin()),
9113 ExitBB);
9114 if (!AfterIP)
9115 return AfterIP.takeError();
9116 Builder.SetInsertPoint(ExitBB);
9117
9118 // Insert target deinit call in the device compilation pass.
9119 if (OMPBuilder.Config.isTargetDevice())
9120 OMPBuilder.createTargetDeinit(Builder);
9121
9122 // Insert return instruction.
9123 Builder.CreateRetVoid();
9124
9125 // New Alloca IP at entry point of created device function.
9126 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9127 auto AllocaIP = Builder.saveIP();
9128
9129 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());
9130
9131 // Do not include the artificial dyn_ptr argument.
9132 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9133
9135
9136 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9137 // Things like GEP's can come in the form of Constants. Constants and
9138 // ConstantExpr's do not have access to the knowledge of what they're
9139 // contained in, so we must dig a little to find an instruction so we
9140 // can tell if they're used inside of the function we're outlining. We
9141 // also replace the original constant expression with a new instruction
9142 // equivalent; an instruction as it allows easy modification in the
9143 // following loop, as we can now know the constant (instruction) is
9144 // owned by our target function and replaceUsesOfWith can now be invoked
9145 // on it (cannot do this with constants it seems). A brand new one also
9146 // allows us to be cautious as it is perhaps possible the old expression
9147 // was used inside of the function but exists and is used externally
9148 // (unlikely by the nature of a Constant, but still).
9149 // NOTE: We cannot remove dead constants that have been rewritten to
9150 // instructions at this stage, we run the risk of breaking later lowering
9151 // by doing so as we could still be in the process of lowering the module
9152 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9153 // constants we have created rewritten versions of.
9154 if (auto *Const = dyn_cast<Constant>(Input))
9155 convertUsersOfConstantsToInstructions(Const, Func, false);
9156
9157 // Collect users before iterating over them to avoid invalidating the
9158 // iteration in case a user uses Input more than once (e.g. a call
9159 // instruction).
9160 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9161 // Collect all the instructions
9163 if (auto *Instr = dyn_cast<Instruction>(User))
9164 if (Instr->getFunction() == Func)
9165 Instr->replaceUsesOfWith(Input, InputCopy);
9166 };
9167
9168 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9169
9170 // Rewrite uses of input valus to parameters.
9171 for (auto InArg : zip(Inputs, ArgRange)) {
9172 Value *Input = std::get<0>(InArg);
9173 Argument &Arg = std::get<1>(InArg);
9174 Value *InputCopy = nullptr;
9175
9176 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9177 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(),
9178 OpenMPIRBuilder::InsertPointTy(ExitBB, ExitBB->begin()));
9179 if (!AfterIP)
9180 return AfterIP.takeError();
9181 Builder.restoreIP(*AfterIP);
9182 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9183
9184 // In certain cases a Global may be set up for replacement, however, this
9185 // Global may be used in multiple arguments to the kernel, just segmented
9186 // apart, for example, if we have a global array, that is sectioned into
9187 // multiple mappings (technically not legal in OpenMP, but there is a case
9188 // in Fortran for Common Blocks where this is neccesary), we will end up
9189 // with GEP's into this array inside the kernel, that refer to the Global
9190 // but are technically separate arguments to the kernel for all intents and
9191 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9192 // index, it will fold into an referal to the Global, if we then encounter
9193 // this folded GEP during replacement all of the references to the
9194 // Global in the kernel will be replaced with the argument we have generated
9195 // that corresponds to it, including any other GEP's that refer to the
9196 // Global that may be other arguments. This will invalidate all of the other
9197 // preceding mapped arguments that refer to the same global that may be
9198 // separate segments. To prevent this, we defer global processing until all
9199 // other processing has been performed.
9202 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9203 continue;
9204 }
9205
9207 continue;
9208
9209 ReplaceValue(Input, InputCopy, Func);
9210 }
9211
9212 // Replace all of our deferred Input values, currently just Globals.
9213 for (auto Deferred : DeferredReplacement)
9214 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9215
9216 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9217 ValueReplacementMap);
9218 return Func;
9219}
9220/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9221/// of pointers containing shared data between the parent task and the created
9222/// task.
9224 IRBuilderBase &Builder,
9225 Value *TaskWithPrivates,
9226 Type *TaskWithPrivatesTy) {
9227
9228 Type *TaskTy = OMPIRBuilder.Task;
9229 LLVMContext &Ctx = Builder.getContext();
9230 Value *TaskT =
9231 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9232 Value *Shareds = TaskT;
9233 // TaskWithPrivatesTy can be one of the following
9234 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9235 // %struct.privates }
9236 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9237 //
9238 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9239 // its first member has to be the task descriptor. TaskTy is the type of the
9240 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9241 // first member of TaskT, gives us the pointer to shared data.
9242 if (TaskWithPrivatesTy != TaskTy)
9243 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9244 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9245}
9246/// Create an entry point for a target task with the following.
9247/// It'll have the following signature
9248/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9249/// This function is called from emitTargetTask once the
9250/// code to launch the target kernel has been outlined already.
9251/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9252/// into the task structure so that the deferred target task can access this
9253/// data even after the stack frame of the generating task has been rolled
9254/// back. Offloading arrays contain base pointers, pointers, sizes etc
9255/// of the data that the target kernel will access. These in effect are the
9256/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9258 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9259 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9260 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9261
9262 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9263 // This is because PrivatesTy is the type of the structure in which
9264 // we pass the offloading arrays to the deferred target task.
9265 assert((!NumOffloadingArrays || PrivatesTy) &&
9266 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9267 "to privatize");
9268
9269 Module &M = OMPBuilder.M;
9270 // KernelLaunchFunction is the target launch function, i.e.
9271 // the function that sets up kernel arguments and calls
9272 // __tgt_target_kernel to launch the kernel on the device.
9273 //
9274 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9275
9276 // StaleCI is the CallInst which is the call to the outlined
9277 // target kernel launch function. If there are local live-in values
9278 // that the outlined function uses then these are aggregated into a structure
9279 // which is passed as the second argument. If there are no local live-in
9280 // values or if all values used by the outlined kernel are global variables,
9281 // then there's only one argument, the threadID. So, StaleCI can be
9282 //
9283 // %structArg = alloca { ptr, ptr }, align 8
9284 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9285 // store ptr %20, ptr %gep_, align 8
9286 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9287 // store ptr %21, ptr %gep_8, align 8
9288 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9289 //
9290 // OR
9291 //
9292 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9294 StaleCI->getIterator());
9295
9296 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9297
9298 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9299 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9300 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9301
9302 auto ProxyFnTy =
9303 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9304 /* isVarArg */ false);
9305 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9306 ".omp_target_task_proxy_func",
9307 Builder.GetInsertBlock()->getModule());
9308 Value *ThreadId = ProxyFn->getArg(0);
9309 Value *TaskWithPrivates = ProxyFn->getArg(1);
9310 ThreadId->setName("thread.id");
9311 TaskWithPrivates->setName("task");
9312
9313 bool HasShareds = SharedArgsOperandNo > 0;
9314 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9315 BasicBlock *EntryBB =
9316 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9317 Builder.SetInsertPoint(EntryBB);
9318
9319 SmallVector<Value *> KernelLaunchArgs;
9320 KernelLaunchArgs.reserve(StaleCI->arg_size());
9321 KernelLaunchArgs.push_back(ThreadId);
9322
9323 if (HasOffloadingArrays) {
9324 assert(TaskTy != TaskWithPrivatesTy &&
9325 "If there are offloading arrays to pass to the target"
9326 "TaskTy cannot be the same as TaskWithPrivatesTy");
9327 (void)TaskTy;
9328 Value *Privates =
9329 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9330 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9331 KernelLaunchArgs.push_back(
9332 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9333 }
9334
9335 if (HasShareds) {
9336 auto *ArgStructAlloca =
9337 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9338 assert(ArgStructAlloca &&
9339 "Unable to find the alloca instruction corresponding to arguments "
9340 "for extracted function");
9341 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9342 std::optional<TypeSize> ArgAllocSize =
9343 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9344 assert(ArgStructType && ArgAllocSize &&
9345 "Unable to determine size of arguments for extracted function");
9346 uint64_t StructSize = ArgAllocSize->getFixedValue();
9347
9348 AllocaInst *NewArgStructAlloca =
9349 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9350
9351 Value *SharedsSize = Builder.getInt64(StructSize);
9352
9354 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9355
9356 Builder.CreateMemCpy(
9357 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9358 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9359 KernelLaunchArgs.push_back(NewArgStructAlloca);
9360 }
9361 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9362 Builder.CreateRetVoid();
9363 return ProxyFn;
9364}
9366
9367 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9368 return GEP->getSourceElementType();
9369 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9370 return Alloca->getAllocatedType();
9371
9372 llvm_unreachable("Unhandled Instruction type");
9373 return nullptr;
9374}
9375// This function returns a struct that has at most two members.
9376// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9377// descriptor. The second member, if needed, is a struct containing arrays
9378// that need to be passed to the offloaded target kernel. For example,
9379// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9380// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9381// respectively, then the types created by this function are
9382//
9383// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9384// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9385// %struct.privates }
9386// %struct.task_with_privates is returned by this function.
9387// If there aren't any offloading arrays to pass to the target kernel,
9388// %struct.kmp_task_ompbuilder_t is returned.
9389static StructType *
9391 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9392
9393 if (OffloadingArraysToPrivatize.empty())
9394 return OMPIRBuilder.Task;
9395
9396 SmallVector<Type *, 4> StructFieldTypes;
9397 for (Value *V : OffloadingArraysToPrivatize) {
9398 assert(V->getType()->isPointerTy() &&
9399 "Expected pointer to array to privatize. Got a non-pointer value "
9400 "instead");
9401 Type *ArrayTy = getOffloadingArrayType(V);
9402 assert(ArrayTy && "ArrayType cannot be nullptr");
9403 StructFieldTypes.push_back(ArrayTy);
9404 }
9405 StructType *PrivatesStructTy =
9406 StructType::create(StructFieldTypes, "struct.privates");
9407 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9408 "struct.task_with_privates");
9409}
9411 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9412 TargetRegionEntryInfo &EntryInfo,
9414 Function *&OutlinedFn, Constant *&OutlinedFnID,
9418
9419 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9420 [&](StringRef EntryFnName) {
9421 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9422 EntryFnName, Inputs, CBFunc,
9423 ArgAccessorFuncCB);
9424 };
9425
9426 return OMPBuilder.emitTargetRegionFunction(
9427 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9428 OutlinedFnID);
9429}
9430
9432 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9434 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9435 bool HasNoWait) {
9436
9437 // The following explains the code-gen scenario for the `target` directive. A
9438 // similar scneario is followed for other device-related directives (e.g.
9439 // `target enter data`) but in similar fashion since we only need to emit task
9440 // that encapsulates the proper runtime call.
9441 //
9442 // When we arrive at this function, the target region itself has been
9443 // outlined into the function OutlinedFn.
9444 // So at ths point, for
9445 // --------------------------------------------------------------
9446 // void user_code_that_offloads(...) {
9447 // omp target depend(..) map(from:a) map(to:b) private(i)
9448 // do i = 1, 10
9449 // a(i) = b(i) + n
9450 // }
9451 //
9452 // --------------------------------------------------------------
9453 //
9454 // we have
9455 //
9456 // --------------------------------------------------------------
9457 //
9458 // void user_code_that_offloads(...) {
9459 // %.offload_baseptrs = alloca [2 x ptr], align 8
9460 // %.offload_ptrs = alloca [2 x ptr], align 8
9461 // %.offload_mappers = alloca [2 x ptr], align 8
9462 // ;; target region has been outlined and now we need to
9463 // ;; offload to it via a target task.
9464 // }
9465 // void outlined_device_function(ptr a, ptr b, ptr n) {
9466 // n = *n_ptr;
9467 // do i = 1, 10
9468 // a(i) = b(i) + n
9469 // }
9470 //
9471 // We have to now do the following
9472 // (i) Make an offloading call to outlined_device_function using the OpenMP
9473 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9474 // emitted by emitKernelLaunch
9475 // (ii) Create a task entry point function that calls kernel_launch_function
9476 // and is the entry point for the target task. See
9477 // '@.omp_target_task_proxy_func in the pseudocode below.
9478 // (iii) Create a task with the task entry point created in (ii)
9479 //
9480 // That is we create the following
9481 // struct task_with_privates {
9482 // struct kmp_task_ompbuilder_t task_struct;
9483 // struct privates {
9484 // [2 x ptr] ; baseptrs
9485 // [2 x ptr] ; ptrs
9486 // [2 x i64] ; sizes
9487 // }
9488 // }
9489 // void user_code_that_offloads(...) {
9490 // %.offload_baseptrs = alloca [2 x ptr], align 8
9491 // %.offload_ptrs = alloca [2 x ptr], align 8
9492 // %.offload_sizes = alloca [2 x i64], align 8
9493 //
9494 // %structArg = alloca { ptr, ptr, ptr }, align 8
9495 // %strucArg[0] = a
9496 // %strucArg[1] = b
9497 // %strucArg[2] = &n
9498 //
9499 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9500 // sizeof(kmp_task_ompbuilder_t),
9501 // sizeof(structArg),
9502 // @.omp_target_task_proxy_func,
9503 // ...)
9504 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9505 // sizeof(structArg))
9506 // memcpy(target_task_with_privates->privates->baseptrs,
9507 // offload_baseptrs, sizeof(offload_baseptrs)
9508 // memcpy(target_task_with_privates->privates->ptrs,
9509 // offload_ptrs, sizeof(offload_ptrs)
9510 // memcpy(target_task_with_privates->privates->sizes,
9511 // offload_sizes, sizeof(offload_sizes)
9512 // dependencies_array = ...
9513 // ;; if nowait not present
9514 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9515 // call @__kmpc_omp_task_begin_if0(...)
9516 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9517 // %target_task_with_privates)
9518 // call @__kmpc_omp_task_complete_if0(...)
9519 // }
9520 //
9521 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9522 // ptr %task) {
9523 // %structArg = alloca {ptr, ptr, ptr}
9524 // %task_ptr = getelementptr(%task, 0, 0)
9525 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9526 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9527 //
9528 // %offloading_arrays = getelementptr(%task, 0, 1)
9529 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9530 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9531 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9532 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9533 // %offload_sizes, %structArg)
9534 // }
9535 //
9536 // We need the proxy function because the signature of the task entry point
9537 // expected by kmpc_omp_task is always the same and will be different from
9538 // that of the kernel_launch function.
9539 //
9540 // kernel_launch_function is generated by emitKernelLaunch and has the
9541 // always_inline attribute. For this example, it'll look like so:
9542 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9543 // %offload_sizes, %structArg) alwaysinline {
9544 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9545 // ; load aggregated data from %structArg
9546 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9547 // ; offload_sizes
9548 // call i32 @__tgt_target_kernel(...,
9549 // outlined_device_function,
9550 // ptr %kernel_args)
9551 // }
9552 // void outlined_device_function(ptr a, ptr b, ptr n) {
9553 // n = *n_ptr;
9554 // do i = 1, 10
9555 // a(i) = b(i) + n
9556 // }
9557 //
9558 BasicBlock *TargetTaskBodyBB =
9559 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9560 BasicBlock *TargetTaskAllocaBB =
9561 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9562
9563 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB,
9564 TargetTaskAllocaBB->begin());
9565 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB, TargetTaskBodyBB->begin());
9566
9567 auto OI = std::make_unique<OutlineInfo>();
9568 OI->EntryBB = TargetTaskAllocaBB;
9569 OI->OuterAllocBB = AllocaIP.getBlock();
9570
9571 // Add the thread ID argument.
9573 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9574 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9575
9576 // Generate the task body which will subsequently be outlined.
9577 Builder.restoreIP(TargetTaskBodyIP);
9578 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9579 return Err;
9580
9581 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9582 // it is given. These blocks are enumerated by
9583 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9584 // to be outside the region. In other words, OI.ExitBlock is expected to be
9585 // the start of the region after the outlining. We used to set OI.ExitBlock
9586 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9587 // except when the task body is a single basic block. In that case,
9588 // OI.ExitBlock is set to the single task body block and will get left out of
9589 // the outlining process. So, simply create a new empty block to which we
9590 // uncoditionally branch from where TaskBodyCB left off
9591 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9592 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9593 /*IsFinished=*/true);
9594
9595 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9596 bool NeedsTargetTask = HasNoWait && DeviceID;
9597 if (NeedsTargetTask) {
9598 for (auto *V :
9599 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9600 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9601 RTArgs.SizesArray}) {
9603 OffloadingArraysToPrivatize.push_back(V);
9604 OI->ExcludeArgsFromAggregate.push_back(V);
9605 }
9606 }
9607 }
9608 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9609 DeviceID, OffloadingArraysToPrivatize](
9610 Function &OutlinedFn) mutable {
9611 assert(OutlinedFn.hasOneUse() &&
9612 "there must be a single user for the outlined function");
9613
9614 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9615
9616 // The first argument of StaleCI is always the thread id.
9617 // The next few arguments are the pointers to offloading arrays
9618 // if any. (see OffloadingArraysToPrivatize)
9619 // Finally, all other local values that are live-in into the outlined region
9620 // end up in a structure whose pointer is passed as the last argument. This
9621 // piece of data is passed in the "shared" field of the task structure. So,
9622 // we know we have to pass shareds to the task if the number of arguments is
9623 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9624 // thread id. Further, for safety, we assert that the number of arguments of
9625 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9626 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9627 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9628 assert((!HasShareds ||
9629 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9630 "Wrong number of arguments for StaleCI when shareds are present");
9631 int SharedArgOperandNo =
9632 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9633
9634 StructType *TaskWithPrivatesTy =
9635 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9636 StructType *PrivatesTy = nullptr;
9637
9638 if (!OffloadingArraysToPrivatize.empty())
9639 PrivatesTy =
9640 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9641
9643 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9644 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9645
9646 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9647 << "\n");
9648
9649 Builder.SetInsertPoint(StaleCI);
9650
9651 // Gather the arguments for emitting the runtime call.
9652 uint32_t SrcLocStrSize;
9653 Constant *SrcLocStr =
9655 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9656
9657 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9658 //
9659 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9660 // the DeviceID to the deferred task and also since
9661 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9662 Function *TaskAllocFn =
9663 !NeedsTargetTask
9664 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9666 OMPRTL___kmpc_omp_target_task_alloc);
9667
9668 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9669 // call.
9670 Value *ThreadID = getOrCreateThreadID(Ident);
9671
9672 // Argument - `sizeof_kmp_task_t` (TaskSize)
9673 // Tasksize refers to the size in bytes of kmp_task_t data structure
9674 // plus any other data to be passed to the target task, if any, which
9675 // is packed into a struct. kmp_task_t and the struct so created are
9676 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9677 Value *TaskSize = Builder.getInt64(
9678 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9679
9680 // Argument - `sizeof_shareds` (SharedsSize)
9681 // SharedsSize refers to the shareds array size in the kmp_task_t data
9682 // structure.
9683 Value *SharedsSize = Builder.getInt64(0);
9684 if (HasShareds) {
9685 auto *ArgStructAlloca =
9686 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9687 assert(ArgStructAlloca &&
9688 "Unable to find the alloca instruction corresponding to arguments "
9689 "for extracted function");
9690 std::optional<TypeSize> ArgAllocSize =
9691 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9692 assert(ArgAllocSize &&
9693 "Unable to determine size of arguments for extracted function");
9694 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9695 }
9696
9697 // Argument - `flags`
9698 // Task is tied iff (Flags & 1) == 1.
9699 // Task is untied iff (Flags & 1) == 0.
9700 // Task is final iff (Flags & 2) == 2.
9701 // Task is not final iff (Flags & 2) == 0.
9702 // A target task is not final and is untied.
9703 Value *Flags = Builder.getInt32(0);
9704
9705 // Emit the @__kmpc_omp_task_alloc runtime call
9706 // The runtime call returns a pointer to an area where the task captured
9707 // variables must be copied before the task is run (TaskData)
9708 CallInst *TaskData = nullptr;
9709
9710 SmallVector<llvm::Value *> TaskAllocArgs = {
9711 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
9712 /*flags=*/Flags,
9713 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
9714 /*task_func=*/ProxyFn};
9715
9716 if (NeedsTargetTask) {
9717 assert(DeviceID && "Expected non-empty device ID.");
9718 TaskAllocArgs.push_back(DeviceID);
9719 }
9720
9721 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
9722
9723 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
9724 if (HasShareds) {
9725 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
9727 *this, Builder, TaskData, TaskWithPrivatesTy);
9728 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
9729 SharedsSize);
9730 }
9731 if (!OffloadingArraysToPrivatize.empty()) {
9732 Value *Privates =
9733 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
9734 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
9735 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
9736 [[maybe_unused]] Type *ArrayType =
9737 getOffloadingArrayType(PtrToPrivatize);
9738 assert(ArrayType && "ArrayType cannot be nullptr");
9739
9740 Type *ElementType = PrivatesTy->getElementType(i);
9741 assert(ElementType == ArrayType &&
9742 "ElementType should match ArrayType");
9743 (void)ArrayType;
9744
9745 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
9746 Builder.CreateMemCpy(
9747 Dst, Alignment, PtrToPrivatize, Alignment,
9748 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
9749 }
9750 }
9751
9752 Value *DepArray = nullptr;
9753 Value *NumDeps = nullptr;
9754 if (Dependencies.DepArray) {
9755 DepArray = Dependencies.DepArray;
9756 NumDeps = Dependencies.NumDeps;
9757 } else if (!Dependencies.Deps.empty()) {
9758 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
9759 NumDeps = Builder.getInt32(Dependencies.Deps.size());
9760 }
9761
9762 // ---------------------------------------------------------------
9763 // V5.2 13.8 target construct
9764 // If the nowait clause is present, execution of the target task
9765 // may be deferred. If the nowait clause is not present, the target task is
9766 // an included task.
9767 // ---------------------------------------------------------------
9768 // The above means that the lack of a nowait on the target construct
9769 // translates to '#pragma omp task if(0)'
9770 if (!NeedsTargetTask) {
9771 if (DepArray) {
9772 Function *TaskWaitFn =
9773 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
9775 TaskWaitFn,
9776 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
9777 /*ndeps=*/NumDeps,
9778 /*dep_list=*/DepArray,
9779 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
9780 /*noalias_dep_list=*/
9782 }
9783 // Included task.
9784 Function *TaskBeginFn =
9785 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
9786 Function *TaskCompleteFn =
9787 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
9788 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
9789 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
9790 CI->setDebugLoc(StaleCI->getDebugLoc());
9791 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
9792 } else if (DepArray) {
9793 // HasNoWait - meaning the task may be deferred. Call
9794 // __kmpc_omp_task_with_deps if there are dependencies,
9795 // else call __kmpc_omp_task
9796 Function *TaskFn =
9797 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
9799 TaskFn,
9800 {Ident, ThreadID, TaskData, NumDeps, DepArray,
9801 ConstantInt::get(Builder.getInt32Ty(), 0),
9803 } else {
9804 // Emit the @__kmpc_omp_task runtime call to spawn the task
9805 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
9806 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
9807 }
9808
9809 StaleCI->eraseFromParent();
9810 for (Instruction *I : llvm::reverse(ToBeDeleted))
9811 I->eraseFromParent();
9812 };
9813 addOutlineInfo(std::move(OI));
9814
9815 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
9816 << *(Builder.GetInsertBlock()) << "\n");
9817 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
9818 << *(Builder.GetInsertBlock()->getParent()->getParent())
9819 << "\n");
9820 return Builder.saveIP();
9821}
9822
9824 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
9825 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
9826 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
9827 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
9828 if (Error Err =
9829 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
9830 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
9831 return Err;
9832 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
9833 return Error::success();
9834}
9835
9836static void emitTargetCall(
9837 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9842 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
9846 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
9847 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
9848 // Generate a function call to the host fallback implementation of the target
9849 // region. This is called by the host when no offload entry was generated for
9850 // the target region and when the offloading call fails at runtime.
9851 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
9853 Builder.restoreIP(IP);
9854 // Ensure the host fallback has the same dyn_ptr ABI as the device.
9855 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
9856 FallbackArgs.push_back(
9857 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
9858 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
9859 return Builder.saveIP();
9860 };
9861
9862 bool HasDependencies = !Dependencies.empty();
9863 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
9864
9866
9867 auto TaskBodyCB =
9868 [&](Value *DeviceID, Value *RTLoc,
9869 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
9870 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
9871 // produce any.
9873 // emitKernelLaunch makes the necessary runtime call to offload the
9874 // kernel. We then outline all that code into a separate function
9875 // ('kernel_launch_function' in the pseudo code above). This function is
9876 // then called by the target task proxy function (see
9877 // '@.omp_target_task_proxy_func' in the pseudo code above)
9878 // "@.omp_target_task_proxy_func' is generated by
9879 // emitTargetTaskProxyFunction.
9880 if (OutlinedFnID && DeviceID)
9881 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
9882 EmitTargetCallFallbackCB, KArgs,
9883 DeviceID, RTLoc, TargetTaskAllocaIP);
9884
9885 // We only need to do the outlining if `DeviceID` is set to avoid calling
9886 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
9887 // generating the `else` branch of an `if` clause.
9888 //
9889 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
9890 // In this case, we execute the host implementation directly.
9891 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
9892 }());
9893
9894 OMPBuilder.Builder.restoreIP(AfterIP);
9895 return Error::success();
9896 };
9897
9898 auto &&EmitTargetCallElse =
9899 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
9901 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9902 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
9903 // produce any.
9905 if (RequiresOuterTargetTask) {
9906 // Arguments that are intended to be directly forwarded to an
9907 // emitKernelLaunch call are pased as nullptr, since
9908 // OutlinedFnID=nullptr results in that call not being done.
9910 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
9911 /*RTLoc=*/nullptr, AllocaIP,
9912 Dependencies, EmptyRTArgs, HasNoWait);
9913 }
9914 return EmitTargetCallFallbackCB(Builder.saveIP());
9915 }());
9916
9917 Builder.restoreIP(AfterIP);
9918 return Error::success();
9919 };
9920
9921 auto &&EmitTargetCallThen =
9922 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
9924 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9925 Info.HasNoWait = HasNoWait;
9926 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
9927
9929 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
9930 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
9931 /*IsNonContiguous=*/true,
9932 /*ForEndCall=*/false))
9933 return Err;
9934
9935 SmallVector<Value *, 3> NumTeamsC;
9936 for (auto [DefaultVal, RuntimeVal] :
9937 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
9938 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
9939 : Builder.getInt32(DefaultVal));
9940
9941 // Calculate number of threads: 0 if no clauses specified, otherwise it is
9942 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
9943 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
9944 if (Clause)
9945 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
9946 /*isSigned=*/false);
9947 return Clause;
9948 };
9949 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
9950 if (Clause)
9951 Result =
9952 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
9953 Result, Clause)
9954 : Clause;
9955 };
9956
9957 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
9958 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
9959 SmallVector<Value *, 3> NumThreadsC;
9960 Value *MaxThreadsClause =
9961 RuntimeAttrs.TeamsThreadLimit.size() == 1
9962 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads)
9963 : nullptr;
9964
9965 for (auto [TeamsVal, TargetVal] : zip_equal(
9966 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
9967 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
9968 Value *NumThreads = InitMaxThreadsClause(TargetVal);
9969
9970 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
9971 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
9972
9973 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
9974 }
9975
9976 unsigned NumTargetItems = Info.NumberOfPtrs;
9977 uint32_t SrcLocStrSize;
9978 Constant *SrcLocStr = OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
9979 Value *RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
9980 llvm::omp::IdentFlag(0), 0);
9981
9982 Value *TripCount = RuntimeAttrs.LoopTripCount
9983 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
9984 Builder.getInt64Ty(),
9985 /*isSigned=*/false)
9986 : Builder.getInt64(0);
9987
9988 // Request zero groupprivate bytes by default.
9989 if (!DynCGroupMem)
9990 DynCGroupMem = Builder.getInt32(0);
9991
9993 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
9994 HasNoWait, /*StrictBlocksAndThreads=*/false, DynCGroupMemFallback);
9995
9996 // Assume no error was returned because TaskBodyCB and
9997 // EmitTargetCallFallbackCB don't produce any.
9999 // The presence of certain clauses on the target directive require the
10000 // explicit generation of the target task.
10001 if (RequiresOuterTargetTask)
10002 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10003 RTLoc, AllocaIP, Dependencies,
10004 KArgs.RTArgs, Info.HasNoWait);
10005
10006 return OMPBuilder.emitKernelLaunch(
10007 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10008 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10009 }());
10010
10011 Builder.restoreIP(AfterIP);
10012 return Error::success();
10013 };
10014
10015 // If we don't have an ID for the target region, it means an offload entry
10016 // wasn't created. In this case we just run the host fallback directly and
10017 // ignore any potential 'if' clauses.
10018 if (!OutlinedFnID) {
10019 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10020 return;
10021 }
10022
10023 // If there's no 'if' clause, only generate the kernel launch code path.
10024 if (!IfCond) {
10025 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10026 return;
10027 }
10028
10029 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10030 EmitTargetCallElse, AllocaIP));
10031}
10032
10034 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10035 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10036 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10037 const TargetKernelDefaultAttrs &DefaultAttrs,
10038 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10039 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10042 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10043 bool HasNowait, Value *DynCGroupMem,
10044 OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10045
10046 if (!updateToLocation(Loc))
10047 return InsertPointTy();
10048
10049 Builder.restoreIP(CodeGenIP);
10050
10051 Function *OutlinedFn;
10052 Constant *OutlinedFnID = nullptr;
10053 // The target region is outlined into its own function. The LLVM IR for
10054 // the target region itself is generated using the callbacks CBFunc
10055 // and ArgAccessorFuncCB
10057 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10058 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB))
10059 return Err;
10060
10061 // If we are not on the target device, then we need to generate code
10062 // to make a remote call (offload) to the previously outlined function
10063 // that represents the target region. Do that now.
10064 if (!Config.isTargetDevice())
10065 emitTargetCall(*this, Builder, AllocaIP, DeallocBlocks, Info, DefaultAttrs,
10066 RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID, Inputs,
10067 GenMapInfoCB, CustomMapperCB, Dependencies, HasNowait,
10068 DynCGroupMem, DynCGroupMemFallback);
10069 return Builder.saveIP();
10070}
10071
10072std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10073 StringRef FirstSeparator,
10074 StringRef Separator) {
10075 SmallString<128> Buffer;
10076 llvm::raw_svector_ostream OS(Buffer);
10077 StringRef Sep = FirstSeparator;
10078 for (StringRef Part : Parts) {
10079 OS << Sep << Part;
10080 Sep = Separator;
10081 }
10082 return OS.str().str();
10083}
10084
10085std::string
10087 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10088 Config.separator());
10089}
10090
10092 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10093 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10094 if (Elem.second) {
10095 assert(Elem.second->getValueType() == Ty &&
10096 "OMP internal variable has different type than requested");
10097 } else {
10098 // TODO: investigate the appropriate linkage type used for the global
10099 // variable for possibly changing that to internal or private, or maybe
10100 // create different versions of the function for different OMP internal
10101 // variables.
10102 const DataLayout &DL = M.getDataLayout();
10103 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10104 // default global AS is 1.
10105 // See double-target-call-with-declare-target.f90 and
10106 // declare-target-vars-in-target-region.f90 libomptarget
10107 // tests.
10108 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10109 : M.getTargetTriple().isAMDGPU()
10110 ? 0
10111 : DL.getDefaultGlobalsAddressSpace();
10112 auto Linkage = this->M.getTargetTriple().getArch() == Triple::wasm32
10115 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10116 Constant::getNullValue(Ty), Elem.first(),
10117 /*InsertBefore=*/nullptr,
10118 GlobalValue::NotThreadLocal, AddressSpaceVal);
10119 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10120 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10121 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10122 Elem.second = GV;
10123 }
10124
10125 return Elem.second;
10126}
10127
10128Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10129 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10130 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10131 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10132}
10133
10135 LLVMContext &Ctx = Builder.getContext();
10136 Value *Null =
10137 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10138 Value *SizeGep =
10139 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10140 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10141 return SizePtrToInt;
10142}
10143
10146 std::string VarName) {
10147 llvm::Constant *MaptypesArrayInit =
10148 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10149 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10150 M, MaptypesArrayInit->getType(),
10151 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10152 VarName);
10153 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10154 return MaptypesArrayGlobal;
10155}
10156
10158 InsertPointTy AllocaIP,
10159 unsigned NumOperands,
10160 struct MapperAllocas &MapperAllocas) {
10161 if (!updateToLocation(Loc))
10162 return;
10163
10164 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10165 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10166 Builder.restoreIP(AllocaIP);
10167 AllocaInst *ArgsBase = Builder.CreateAlloca(
10168 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10169 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10170 ".offload_ptrs");
10171 AllocaInst *ArgSizes = Builder.CreateAlloca(
10172 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10174 MapperAllocas.ArgsBase = ArgsBase;
10175 MapperAllocas.Args = Args;
10176 MapperAllocas.ArgSizes = ArgSizes;
10177}
10178
10180 Function *MapperFunc, Value *SrcLocInfo,
10181 Value *MaptypesArg, Value *MapnamesArg,
10183 int64_t DeviceID, unsigned NumOperands) {
10184 if (!updateToLocation(Loc))
10185 return;
10186
10187 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10188 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10189 Value *ArgsBaseGEP =
10190 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10191 {Builder.getInt32(0), Builder.getInt32(0)});
10192 Value *ArgsGEP =
10193 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10194 {Builder.getInt32(0), Builder.getInt32(0)});
10195 Value *ArgSizesGEP =
10196 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10197 {Builder.getInt32(0), Builder.getInt32(0)});
10198 Value *NullPtr =
10199 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10200 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10201 Builder.getInt32(NumOperands),
10202 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10203 MaptypesArg, MapnamesArg, NullPtr});
10204}
10205
10207 TargetDataRTArgs &RTArgs,
10208 TargetDataInfo &Info,
10209 bool ForEndCall) {
10210 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10211 "expected region end call to runtime only when end call is separate");
10212 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10213 auto VoidPtrTy = UnqualPtrTy;
10214 auto VoidPtrPtrTy = UnqualPtrTy;
10215 auto Int64Ty = Type::getInt64Ty(M.getContext());
10216 auto Int64PtrTy = UnqualPtrTy;
10217
10218 if (!Info.NumberOfPtrs) {
10219 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10220 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10221 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10222 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10223 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10224 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10225 return;
10226 }
10227
10228 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10229 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10230 Info.RTArgs.BasePointersArray,
10231 /*Idx0=*/0, /*Idx1=*/0);
10232 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10233 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10234 /*Idx0=*/0,
10235 /*Idx1=*/0);
10236 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10237 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10238 /*Idx0=*/0, /*Idx1=*/0);
10239 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10240 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10241 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10242 : Info.RTArgs.MapTypesArray,
10243 /*Idx0=*/0,
10244 /*Idx1=*/0);
10245
10246 // Only emit the mapper information arrays if debug information is
10247 // requested.
10248 if (!Info.EmitDebug)
10249 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10250 else
10251 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10252 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10253 /*Idx0=*/0,
10254 /*Idx1=*/0);
10255 // If there is no user-defined mapper, set the mapper array to nullptr to
10256 // avoid an unnecessary data privatization
10257 if (!Info.HasMapper)
10258 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10259 else
10260 RTArgs.MappersArray =
10261 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10262}
10263
10265 InsertPointTy CodeGenIP,
10266 MapInfosTy &CombinedInfo,
10267 TargetDataInfo &Info) {
10269 CombinedInfo.NonContigInfo;
10270
10271 // Build an array of struct descriptor_dim and then assign it to
10272 // offload_args.
10273 //
10274 // struct descriptor_dim {
10275 // uint64_t offset;
10276 // uint64_t count;
10277 // uint64_t stride
10278 // };
10279 Type *Int64Ty = Builder.getInt64Ty();
10281 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10282 "struct.descriptor_dim");
10283
10284 enum { OffsetFD = 0, CountFD, StrideFD };
10285 // We need two index variable here since the size of "Dims" is the same as
10286 // the size of Components, however, the size of offset, count, and stride is
10287 // equal to the size of base declaration that is non-contiguous.
10288 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10289 // Skip emitting ir if dimension size is 1 since it cannot be
10290 // non-contiguous.
10291 if (NonContigInfo.Dims[I] == 1)
10292 continue;
10293 Builder.restoreIP(AllocaIP);
10294 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10295 AllocaInst *DimsAddr =
10296 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10297 Builder.restoreIP(CodeGenIP);
10298 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10299 unsigned RevIdx = EE - II - 1;
10300 Value *DimsLVal = Builder.CreateInBoundsGEP(
10301 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10302 // Offset
10303 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10304 Builder.CreateAlignedStore(
10305 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10306 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10307 // Count
10308 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10309 Builder.CreateAlignedStore(
10310 NonContigInfo.Counts[L][RevIdx], CountLVal,
10311 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10312 // Stride
10313 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10314 Builder.CreateAlignedStore(
10315 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10316 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10317 }
10318 // args[I] = &dims
10319 Builder.restoreIP(CodeGenIP);
10320 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10321 DimsAddr, Builder.getPtrTy());
10322 Value *P = Builder.CreateConstInBoundsGEP2_32(
10323 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10324 Info.RTArgs.PointersArray, 0, I);
10325 Builder.CreateAlignedStore(
10326 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10327 ++L;
10328 }
10329}
10330
10331void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10332 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10333 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10334 BasicBlock *ExitBB, bool IsInit) {
10335 StringRef Prefix = IsInit ? ".init" : ".del";
10336
10337 // Evaluate if this is an array section.
10339 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10340 Value *IsArray =
10341 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10342 Value *DeleteBit = Builder.CreateAnd(
10343 MapType,
10344 Builder.getInt64(
10345 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10346 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10347 Value *DeleteCond;
10348 Value *Cond;
10349 if (IsInit) {
10350 // base != begin?
10351 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10352 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10353 DeleteCond = Builder.CreateIsNull(
10354 DeleteBit,
10355 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10356 } else {
10357 Cond = IsArray;
10358 DeleteCond = Builder.CreateIsNotNull(
10359 DeleteBit,
10360 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10361 }
10362 Cond = Builder.CreateAnd(Cond, DeleteCond);
10363 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10364
10365 emitBlock(BodyBB, MapperFn);
10366 // Get the array size by multiplying element size and element number (i.e., \p
10367 // Size).
10368 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10369 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10370 // memory allocation/deletion purpose only.
10371 Value *MapTypeArg = Builder.CreateAnd(
10372 MapType,
10373 Builder.getInt64(
10374 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10375 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10376 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10377 MapTypeArg = Builder.CreateOr(
10378 MapTypeArg,
10379 Builder.getInt64(
10380 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10381 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10382
10383 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10384 // data structure.
10385 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10386 ArraySize, MapTypeArg, MapName};
10388 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10389 OffloadingArgs);
10390}
10391
10394 llvm::Value *BeginArg)>
10395 GenMapInfoCB,
10396 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10397 bool PreserveMemberOfFlags) {
10398 SmallVector<Type *> Params;
10399 Params.emplace_back(Builder.getPtrTy());
10400 Params.emplace_back(Builder.getPtrTy());
10401 Params.emplace_back(Builder.getPtrTy());
10402 Params.emplace_back(Builder.getInt64Ty());
10403 Params.emplace_back(Builder.getInt64Ty());
10404 Params.emplace_back(Builder.getPtrTy());
10405
10406 auto *FnTy =
10407 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10408
10409 SmallString<64> TyStr;
10410 raw_svector_ostream Out(TyStr);
10411 Function *MapperFn =
10413 MapperFn->addFnAttr(Attribute::NoInline);
10414 MapperFn->addFnAttr(Attribute::NoUnwind);
10415 MapperFn->addParamAttr(0, Attribute::NoUndef);
10416 MapperFn->addParamAttr(1, Attribute::NoUndef);
10417 MapperFn->addParamAttr(2, Attribute::NoUndef);
10418 MapperFn->addParamAttr(3, Attribute::NoUndef);
10419 MapperFn->addParamAttr(4, Attribute::NoUndef);
10420 MapperFn->addParamAttr(5, Attribute::NoUndef);
10421
10422 // Start the mapper function code generation.
10423 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10424 auto SavedIP = Builder.saveIP();
10425 Builder.SetInsertPoint(EntryBB);
10426
10427 Value *MapperHandle = MapperFn->getArg(0);
10428 Value *BaseIn = MapperFn->getArg(1);
10429 Value *BeginIn = MapperFn->getArg(2);
10430 Value *Size = MapperFn->getArg(3);
10431 Value *MapType = MapperFn->getArg(4);
10432 Value *MapName = MapperFn->getArg(5);
10433
10434 // Compute the starting and end addresses of array elements.
10435 // Prepare common arguments for array initiation and deletion.
10436 // Convert the size in bytes into the number of array elements.
10437 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10438 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10439 Value *PtrBegin = BeginIn;
10440 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10441
10442 // Emit array initiation if this is an array section and \p MapType indicates
10443 // that memory allocation is required.
10444 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10445 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10446 MapType, MapName, ElementSize, HeadBB,
10447 /*IsInit=*/true);
10448
10449 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10450
10451 // Emit the loop header block.
10452 emitBlock(HeadBB, MapperFn);
10453 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10454 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10455 // Evaluate whether the initial condition is satisfied.
10456 Value *IsEmpty =
10457 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10458 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10459
10460 // Emit the loop body block.
10461 emitBlock(BodyBB, MapperFn);
10462 BasicBlock *LastBB = BodyBB;
10463 PHINode *PtrPHI =
10464 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10465 PtrPHI->addIncoming(PtrBegin, HeadBB);
10466
10467 // Get map clause information. Fill up the arrays with all mapped variables.
10468 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10469 if (!Info)
10470 return Info.takeError();
10471
10472 // Call the runtime API __tgt_mapper_num_components to get the number of
10473 // pre-existing components.
10474 Value *OffloadingArgs[] = {MapperHandle};
10475 Value *PreviousSize = createRuntimeFunctionCall(
10476 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10477 OffloadingArgs);
10478 Value *ShiftedPreviousSize =
10479 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10480
10481 // Fill up the runtime mapper handle for all components.
10482 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10483 Value *CurBaseArg = Info->BasePointers[I];
10484 Value *CurBeginArg = Info->Pointers[I];
10485 Value *CurSizeArg = Info->Sizes[I];
10486 Value *CurNameArg = Info->Names.size()
10487 ? Info->Names[I]
10488 : Constant::getNullValue(Builder.getPtrTy());
10489
10490 // Extract the MEMBER_OF field from the map type.
10491 Value *OriMapType = Builder.getInt64(
10492 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10493 Info->Types[I]));
10494 Value *MemberMapType;
10495 if (PreserveMemberOfFlags) {
10496 constexpr uint64_t MemberOfMask =
10497 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10498 uint64_t OrigFlags =
10499 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10500 Info->Types[I]);
10501 bool HasMemberOf = (OrigFlags & MemberOfMask) != 0;
10502 if (HasMemberOf)
10503 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10504 else
10505 MemberMapType = OriMapType;
10506 } else {
10507 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10508 }
10509
10510 // Combine the map type inherited from user-defined mapper with that
10511 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10512 // bits of the \a MapType, which is the input argument of the mapper
10513 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10514 // bits of MemberMapType.
10515 // [OpenMP 5.0], 1.2.6. map-type decay.
10516 // | alloc | to | from | tofrom | release | delete
10517 // ----------------------------------------------------------
10518 // alloc | alloc | alloc | alloc | alloc | release | delete
10519 // to | alloc | to | alloc | to | release | delete
10520 // from | alloc | alloc | from | from | release | delete
10521 // tofrom | alloc | to | from | tofrom | release | delete
10522 Value *LeftToFrom = Builder.CreateAnd(
10523 MapType,
10524 Builder.getInt64(
10525 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10526 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10527 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10528 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10529 BasicBlock *AllocElseBB =
10530 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10531 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10532 BasicBlock *ToElseBB =
10533 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10534 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10535 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10536 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10537 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10538 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10539 emitBlock(AllocBB, MapperFn);
10540 Value *AllocMapType = Builder.CreateAnd(
10541 MemberMapType,
10542 Builder.getInt64(
10543 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10544 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10545 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10546 Builder.CreateBr(EndBB);
10547 emitBlock(AllocElseBB, MapperFn);
10548 Value *IsTo = Builder.CreateICmpEQ(
10549 LeftToFrom,
10550 Builder.getInt64(
10551 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10552 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10553 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10554 // In case of to, clear OMP_MAP_FROM.
10555 emitBlock(ToBB, MapperFn);
10556 Value *ToMapType = Builder.CreateAnd(
10557 MemberMapType,
10558 Builder.getInt64(
10559 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10560 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10561 Builder.CreateBr(EndBB);
10562 emitBlock(ToElseBB, MapperFn);
10563 Value *IsFrom = Builder.CreateICmpEQ(
10564 LeftToFrom,
10565 Builder.getInt64(
10566 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10567 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10568 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10569 // In case of from, clear OMP_MAP_TO.
10570 emitBlock(FromBB, MapperFn);
10571 Value *FromMapType = Builder.CreateAnd(
10572 MemberMapType,
10573 Builder.getInt64(
10574 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10575 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10576 // In case of tofrom, do nothing.
10577 emitBlock(EndBB, MapperFn);
10578 LastBB = EndBB;
10579 PHINode *CurMapType =
10580 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10581 CurMapType->addIncoming(AllocMapType, AllocBB);
10582 CurMapType->addIncoming(ToMapType, ToBB);
10583 CurMapType->addIncoming(FromMapType, FromBB);
10584 CurMapType->addIncoming(MemberMapType, ToElseBB);
10585
10586 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
10587 CurSizeArg, CurMapType, CurNameArg};
10588
10589 auto ChildMapperFn = CustomMapperCB(I);
10590 if (!ChildMapperFn)
10591 return ChildMapperFn.takeError();
10592 if (*ChildMapperFn) {
10593 // Call the corresponding mapper function.
10594 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
10595 ->setDoesNotThrow();
10596 } else {
10597 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10598 // data structure.
10600 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10601 OffloadingArgs);
10602 }
10603 }
10604
10605 // Update the pointer to point to the next element that needs to be mapped,
10606 // and check whether we have mapped all elements.
10607 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
10608 "omp.arraymap.next");
10609 PtrPHI->addIncoming(PtrNext, LastBB);
10610 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
10611 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
10612 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
10613
10614 emitBlock(ExitBB, MapperFn);
10615 // Emit array deletion if this is an array section and \p MapType indicates
10616 // that deletion is required.
10617 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10618 MapType, MapName, ElementSize, DoneBB,
10619 /*IsInit=*/false);
10620
10621 // Emit the function exit block.
10622 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
10623
10624 Builder.CreateRetVoid();
10625 Builder.restoreIP(SavedIP);
10626 return MapperFn;
10627}
10628
10630 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
10631 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
10632 bool IsNonContiguous,
10633 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10634
10635 // Reset the array information.
10636 Info.clearArrayInfo();
10637 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
10638
10639 if (Info.NumberOfPtrs == 0)
10640 return Error::success();
10641
10642 Builder.restoreIP(AllocaIP);
10643 // Detect if we have any capture size requiring runtime evaluation of the
10644 // size so that a constant array could be eventually used.
10645 ArrayType *PointerArrayType =
10646 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
10647
10648 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
10649 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
10650
10651 Info.RTArgs.PointersArray = Builder.CreateAlloca(
10652 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
10653 AllocaInst *MappersArray = Builder.CreateAlloca(
10654 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
10655 Info.RTArgs.MappersArray = MappersArray;
10656
10657 // If we don't have any VLA types or other types that require runtime
10658 // evaluation, we can use a constant array for the map sizes, otherwise we
10659 // need to fill up the arrays as we do for the pointers.
10660 Type *Int64Ty = Builder.getInt64Ty();
10661 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
10662 ConstantInt::get(Int64Ty, 0));
10663 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
10664 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
10665 bool IsNonContigEntry =
10666 IsNonContiguous &&
10667 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10668 CombinedInfo.Types[I] &
10669 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
10670 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
10671 // descriptor_dim records), not the byte size.
10672 if (IsNonContigEntry) {
10673 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
10674 "Index must be in-bounds for NON_CONTIG Dims array");
10675 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
10676 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
10677 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
10678 continue;
10679 }
10680 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
10681 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
10682 ConstSizes[I] = CI;
10683 continue;
10684 }
10685 }
10686 RuntimeSizes.set(I);
10687 }
10688
10689 if (RuntimeSizes.all()) {
10690 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
10691 Info.RTArgs.SizesArray = Builder.CreateAlloca(
10692 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
10693 restoreIPandDebugLoc(Builder, CodeGenIP);
10694 } else {
10695 auto *SizesArrayInit = ConstantArray::get(
10696 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
10697 std::string Name = createPlatformSpecificName({"offload_sizes"});
10698 auto *SizesArrayGbl =
10699 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
10700 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
10701 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
10702
10703 if (!RuntimeSizes.any()) {
10704 Info.RTArgs.SizesArray = SizesArrayGbl;
10705 } else {
10706 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
10707 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
10708 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
10709 AllocaInst *Buffer = Builder.CreateAlloca(
10710 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
10711 Buffer->setAlignment(OffloadSizeAlign);
10712 restoreIPandDebugLoc(Builder, CodeGenIP);
10713 Builder.CreateMemCpy(
10714 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
10715 SizesArrayGbl, OffloadSizeAlign,
10716 Builder.getIntN(
10717 IndexSize,
10718 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
10719
10720 Info.RTArgs.SizesArray = Buffer;
10721 }
10722 restoreIPandDebugLoc(Builder, CodeGenIP);
10723 }
10724
10725 // The map types are always constant so we don't need to generate code to
10726 // fill arrays. Instead, we create an array constant.
10728 for (auto mapFlag : CombinedInfo.Types)
10729 Mapping.push_back(
10730 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10731 mapFlag));
10732 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
10733 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
10734 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
10735
10736 // The information types are only built if provided.
10737 if (!CombinedInfo.Names.empty()) {
10738 auto *MapNamesArrayGbl = createOffloadMapnames(
10739 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
10740 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
10741 Info.EmitDebug = true;
10742 } else {
10743 Info.RTArgs.MapNamesArray =
10745 Info.EmitDebug = false;
10746 }
10747
10748 // If there's a present map type modifier, it must not be applied to the end
10749 // of a region, so generate a separate map type array in that case.
10750 if (Info.separateBeginEndCalls()) {
10751 bool EndMapTypesDiffer = false;
10752 for (uint64_t &Type : Mapping) {
10753 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10754 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
10755 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10756 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10757 EndMapTypesDiffer = true;
10758 }
10759 }
10760 if (EndMapTypesDiffer) {
10761 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
10762 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
10763 }
10764 }
10765
10766 PointerType *PtrTy = Builder.getPtrTy();
10767 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
10768 Value *BPVal = CombinedInfo.BasePointers[I];
10769 Value *BP = Builder.CreateConstInBoundsGEP2_32(
10770 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
10771 0, I);
10772 Builder.CreateAlignedStore(BPVal, BP,
10773 M.getDataLayout().getPrefTypeAlign(PtrTy));
10774
10775 if (Info.requiresDevicePointerInfo()) {
10776 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
10777 CodeGenIP = Builder.saveIP();
10778 Builder.restoreIP(AllocaIP);
10779 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
10780 Builder.restoreIP(CodeGenIP);
10781 if (DeviceAddrCB)
10782 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
10783 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
10784 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
10785 if (DeviceAddrCB)
10786 DeviceAddrCB(I, BP);
10787 }
10788 }
10789
10790 Value *PVal = CombinedInfo.Pointers[I];
10791 Value *P = Builder.CreateConstInBoundsGEP2_32(
10792 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
10793 I);
10794 // TODO: Check alignment correct.
10795 Builder.CreateAlignedStore(PVal, P,
10796 M.getDataLayout().getPrefTypeAlign(PtrTy));
10797
10798 if (RuntimeSizes.test(I)) {
10799 Value *S = Builder.CreateConstInBoundsGEP2_32(
10800 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10801 /*Idx0=*/0,
10802 /*Idx1=*/I);
10803 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
10804 Int64Ty,
10805 /*isSigned=*/true),
10806 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
10807 }
10808 // Fill up the mapper array.
10809 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
10810 Value *MFunc = ConstantPointerNull::get(PtrTy);
10811
10812 auto CustomMFunc = CustomMapperCB(I);
10813 if (!CustomMFunc)
10814 return CustomMFunc.takeError();
10815 if (*CustomMFunc)
10816 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
10817
10818 Value *MAddr = Builder.CreateInBoundsGEP(
10819 PointerArrayType, MappersArray,
10820 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
10821 Builder.CreateAlignedStore(
10822 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
10823 }
10824
10825 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
10826 Info.NumberOfPtrs == 0)
10827 return Error::success();
10828 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
10829 return Error::success();
10830}
10831
10833 BasicBlock *CurBB = Builder.GetInsertBlock();
10834
10835 if (!CurBB || CurBB->hasTerminator()) {
10836 // If there is no insert point or the previous block is already
10837 // terminated, don't touch it.
10838 } else {
10839 // Otherwise, create a fall-through branch.
10840 Builder.CreateBr(Target);
10841 }
10842
10843 Builder.ClearInsertionPoint();
10844}
10845
10847 bool IsFinished) {
10848 BasicBlock *CurBB = Builder.GetInsertBlock();
10849
10850 // Fall out of the current block (if necessary).
10851 emitBranch(BB);
10852
10853 if (IsFinished && BB->use_empty()) {
10854 BB->eraseFromParent();
10855 return;
10856 }
10857
10858 // Place the block after the current block, if possible, or else at
10859 // the end of the function.
10860 if (CurBB && CurBB->getParent())
10861 CurFn->insert(std::next(CurBB->getIterator()), BB);
10862 else
10863 CurFn->insert(CurFn->end(), BB);
10864 Builder.SetInsertPoint(BB);
10865}
10866
10868 BodyGenCallbackTy ElseGen,
10869 InsertPointTy AllocaIP,
10870 ArrayRef<BasicBlock *> DeallocBlocks) {
10871 // If the condition constant folds and can be elided, try to avoid emitting
10872 // the condition and the dead arm of the if/else.
10873 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
10874 auto CondConstant = CI->getSExtValue();
10875 if (CondConstant)
10876 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
10877
10878 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
10879 }
10880
10881 Function *CurFn = Builder.GetInsertBlock()->getParent();
10882
10883 // Otherwise, the condition did not fold, or we couldn't elide it. Just
10884 // emit the conditional branch.
10885 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
10886 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
10887 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
10888 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
10889 // Emit the 'then' code.
10890 emitBlock(ThenBlock, CurFn);
10891 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
10892 return Err;
10893 emitBranch(ContBlock);
10894 // Emit the 'else' code if present.
10895 // There is no need to emit line number for unconditional branch.
10896 emitBlock(ElseBlock, CurFn);
10897 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
10898 return Err;
10899 // There is no need to emit line number for unconditional branch.
10900 emitBranch(ContBlock);
10901 // Emit the continuation block for code after the if.
10902 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
10903 return Error::success();
10904}
10905
10906bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
10907 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
10910 "Unexpected Atomic Ordering.");
10911
10912 bool Flush = false;
10914
10915 switch (AK) {
10916 case Read:
10919 FlushAO = AtomicOrdering::Acquire;
10920 Flush = true;
10921 }
10922 break;
10923 case Write:
10924 case Compare:
10925 case Update:
10928 FlushAO = AtomicOrdering::Release;
10929 Flush = true;
10930 }
10931 break;
10932 case Capture:
10933 switch (AO) {
10935 FlushAO = AtomicOrdering::Acquire;
10936 Flush = true;
10937 break;
10939 FlushAO = AtomicOrdering::Release;
10940 Flush = true;
10941 break;
10945 Flush = true;
10946 break;
10947 default:
10948 // do nothing - leave silently.
10949 break;
10950 }
10951 }
10952
10953 if (Flush) {
10954 // Currently Flush RT call still doesn't take memory_ordering, so for when
10955 // that happens, this tries to do the resolution of which atomic ordering
10956 // to use with but issue the flush call
10957 // TODO: pass `FlushAO` after memory ordering support is added
10958 (void)FlushAO;
10959 emitFlush(Loc);
10960 }
10961
10962 // for AO == AtomicOrdering::Monotonic and all other case combinations
10963 // do nothing
10964 return Flush;
10965}
10966
10970 AtomicOrdering AO, InsertPointTy AllocaIP) {
10971 if (!updateToLocation(Loc))
10972 return Loc.IP;
10973
10974 assert(X.Var->getType()->isPointerTy() &&
10975 "OMP Atomic expects a pointer to target memory");
10976 Type *XElemTy = X.ElemTy;
10977 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
10978 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
10979 "OMP atomic read expected a scalar type");
10980
10981 Value *XRead = nullptr;
10982
10983 if (XElemTy->isIntegerTy()) {
10984 LoadInst *XLD =
10985 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
10986 XLD->setAtomic(AO);
10987 XRead = cast<Value>(XLD);
10988 } else if (XElemTy->isStructTy()) {
10989 // FIXME: Add checks to ensure __atomic_load is emitted iff the
10990 // target does not support `atomicrmw` of the size of the struct
10991 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
10992 OldVal->setAtomic(AO);
10993 const DataLayout &DL = OldVal->getModule()->getDataLayout();
10994 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
10995 OpenMPIRBuilder::AtomicInfo atomicInfo(
10996 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
10997 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
10998 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
10999 XRead = AtomicLoadRes.first;
11000 OldVal->eraseFromParent();
11001 } else {
11002 // We need to perform atomic op as integer
11003 IntegerType *IntCastTy =
11004 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11005 LoadInst *XLoad =
11006 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11007 XLoad->setAtomic(AO);
11008 if (XElemTy->isFloatingPointTy()) {
11009 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11010 } else {
11011 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11012 }
11013 }
11014 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11015 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11016 return Builder.saveIP();
11017}
11018
11021 AtomicOpValue &X, Value *Expr,
11022 AtomicOrdering AO, InsertPointTy AllocaIP) {
11023 if (!updateToLocation(Loc))
11024 return Loc.IP;
11025
11026 assert(X.Var->getType()->isPointerTy() &&
11027 "OMP Atomic expects a pointer to target memory");
11028 Type *XElemTy = X.ElemTy;
11029 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11030 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11031 "OMP atomic write expected a scalar type");
11032
11033 if (XElemTy->isIntegerTy()) {
11034 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11035 XSt->setAtomic(AO);
11036 } else if (XElemTy->isStructTy()) {
11037 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11038 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11039 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11040 OpenMPIRBuilder::AtomicInfo atomicInfo(
11041 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11042 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11043 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11044 OldVal->eraseFromParent();
11045 } else {
11046 // We need to bitcast and perform atomic op as integers
11047 IntegerType *IntCastTy =
11048 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11049 Value *ExprCast =
11050 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11051 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11052 XSt->setAtomic(AO);
11053 }
11054
11055 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11056 return Builder.saveIP();
11057}
11058
11061 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11062 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11063 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11064 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11065 if (!updateToLocation(Loc))
11066 return Loc.IP;
11067
11068 LLVM_DEBUG({
11069 Type *XTy = X.Var->getType();
11070 assert(XTy->isPointerTy() &&
11071 "OMP Atomic expects a pointer to target memory");
11072 Type *XElemTy = X.ElemTy;
11073 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11074 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11075 "OMP atomic update expected a scalar or struct type");
11076 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11077 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&
11078 "OpenMP atomic does not support LT or GT operations");
11079 });
11080
11081 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11082 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11083 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11084 if (!AtomicResult)
11085 return AtomicResult.takeError();
11086 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11087 return Builder.saveIP();
11088}
11089
11090// FIXME: Duplicating AtomicExpand
11091Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11092 AtomicRMWInst::BinOp RMWOp) {
11093 switch (RMWOp) {
11094 case AtomicRMWInst::Add:
11095 return Builder.CreateAdd(Src1, Src2);
11096 case AtomicRMWInst::Sub:
11097 return Builder.CreateSub(Src1, Src2);
11098 case AtomicRMWInst::And:
11099 return Builder.CreateAnd(Src1, Src2);
11101 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11102 case AtomicRMWInst::Or:
11103 return Builder.CreateOr(Src1, Src2);
11104 case AtomicRMWInst::Xor:
11105 return Builder.CreateXor(Src1, Src2);
11110 case AtomicRMWInst::Max:
11111 case AtomicRMWInst::Min:
11124 llvm_unreachable("Unsupported atomic update operation");
11125 }
11126 llvm_unreachable("Unsupported atomic update operation");
11127}
11128
11130 // Loads cannot use Release or AcquireRelease ordering. This load is
11131 // just the initial value for the cmpxchg loop; the cmpxchg itself
11132 // retains the original ordering.
11133 AtomicOrdering LoadAO = AO;
11134
11135 if (AO == AtomicOrdering::Release) {
11137 } else if (AO == AtomicOrdering::AcquireRelease) {
11138 LoadAO = AtomicOrdering::Acquire;
11139 }
11140
11141 return LoadAO;
11142}
11143
11144Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11145 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11147 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11148 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11149 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11150 bool emitRMWOp = false;
11151 switch (RMWOp) {
11152 case AtomicRMWInst::Add:
11153 case AtomicRMWInst::And:
11155 case AtomicRMWInst::Or:
11156 case AtomicRMWInst::Xor:
11158 emitRMWOp = XElemTy;
11159 break;
11160 case AtomicRMWInst::Sub:
11161 emitRMWOp = (IsXBinopExpr && XElemTy);
11162 break;
11163 default:
11164 emitRMWOp = false;
11165 }
11166 emitRMWOp &= XElemTy->isIntegerTy();
11167
11168 std::pair<Value *, Value *> Res;
11169 if (emitRMWOp) {
11170 AtomicRMWInst *RMWInst =
11171 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11172 if (T.isAMDGPU()) {
11173 if (IsIgnoreDenormalMode)
11174 RMWInst->setMetadata("amdgpu.ignore.denormal.mode",
11175 llvm::MDNode::get(Builder.getContext(), {}));
11176 if (!IsFineGrainedMemory)
11177 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11178 llvm::MDNode::get(Builder.getContext(), {}));
11179 if (!IsRemoteMemory)
11180 RMWInst->setMetadata("amdgpu.no.remote.memory",
11181 llvm::MDNode::get(Builder.getContext(), {}));
11182 }
11183 Res.first = RMWInst;
11184 // not needed except in case of postfix captures. Generate anyway for
11185 // consistency with the else part. Will be removed with any DCE pass.
11186 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11187 if (RMWOp == AtomicRMWInst::Xchg)
11188 Res.second = Res.first;
11189 else
11190 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11191 } else if (XElemTy->isStructTy()) {
11192 LoadInst *OldVal =
11193 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11195 OldVal->setAtomic(LoadAO);
11196 const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
11197 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11198
11199 OpenMPIRBuilder::AtomicInfo atomicInfo(
11200 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11201 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11202 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11203 BasicBlock *CurBB = Builder.GetInsertBlock();
11204 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11205 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11206 BasicBlock *ExitBB =
11207 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11208 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11209 X->getName() + ".atomic.cont");
11210 ContBB->getTerminator()->eraseFromParent();
11211 Builder.restoreIP(AllocaIP);
11212 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11213 NewAtomicAddr->setName(X->getName() + "x.new.val");
11214 Builder.SetInsertPoint(ContBB);
11215 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11216 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11217 Value *OldExprVal = PHI;
11218 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11219 if (!CBResult)
11220 return CBResult.takeError();
11221 Value *Upd = *CBResult;
11222 Builder.CreateStore(Upd, NewAtomicAddr);
11225 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11226 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11227 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11228 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11229 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11230 OldVal->eraseFromParent();
11231 Res.first = OldExprVal;
11232 Res.second = Upd;
11233
11234 if (UnreachableInst *ExitTI =
11236 CurBBTI->eraseFromParent();
11237 Builder.SetInsertPoint(ExitBB);
11238 } else {
11239 Builder.SetInsertPoint(ExitTI);
11240 }
11241 } else {
11242 IntegerType *IntCastTy =
11243 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11244 LoadInst *OldVal =
11245 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11247 OldVal->setAtomic(LoadAO);
11248 // CurBB
11249 // | /---\
11250 // ContBB |
11251 // | \---/
11252 // ExitBB
11253 BasicBlock *CurBB = Builder.GetInsertBlock();
11254 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11255 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11256 BasicBlock *ExitBB =
11257 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11258 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11259 X->getName() + ".atomic.cont");
11260 ContBB->getTerminator()->eraseFromParent();
11261 Builder.restoreIP(AllocaIP);
11262 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11263 NewAtomicAddr->setName(X->getName() + "x.new.val");
11264 Builder.SetInsertPoint(ContBB);
11265 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11266 PHI->addIncoming(OldVal, CurBB);
11267 bool IsIntTy = XElemTy->isIntegerTy();
11268 Value *OldExprVal = PHI;
11269 if (!IsIntTy) {
11270 if (XElemTy->isFloatingPointTy()) {
11271 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11272 X->getName() + ".atomic.fltCast");
11273 } else {
11274 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11275 X->getName() + ".atomic.ptrCast");
11276 }
11277 }
11278
11279 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11280 if (!CBResult)
11281 return CBResult.takeError();
11282 Value *Upd = *CBResult;
11283 Builder.CreateStore(Upd, NewAtomicAddr);
11284 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11287 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11288 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11289 Result->setVolatile(VolatileX);
11290 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11291 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11292 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11293 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11294
11295 Res.first = OldExprVal;
11296 Res.second = Upd;
11297
11298 // set Insertion point in exit block
11299 if (UnreachableInst *ExitTI =
11301 CurBBTI->eraseFromParent();
11302 Builder.SetInsertPoint(ExitBB);
11303 } else {
11304 Builder.SetInsertPoint(ExitTI);
11305 }
11306 }
11307
11308 return Res;
11309}
11310
11313 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11314 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11315 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11316 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11317 if (!updateToLocation(Loc))
11318 return Loc.IP;
11319
11320 LLVM_DEBUG({
11321 Type *XTy = X.Var->getType();
11322 assert(XTy->isPointerTy() &&
11323 "OMP Atomic expects a pointer to target memory");
11324 Type *XElemTy = X.ElemTy;
11325 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11326 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11327 "OMP atomic capture expected a scalar or struct type");
11328 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11329 "OpenMP atomic does not support LT or GT operations");
11330 });
11331
11332 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11333 // 'x' is simply atomically rewritten with 'expr'.
11334 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11335 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11336 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11337 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11338 if (!AtomicResult)
11339 return AtomicResult.takeError();
11340 Value *CapturedVal =
11341 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11342 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11343
11344 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11345 return Builder.saveIP();
11346}
11347
11351 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11352 bool IsFailOnly, bool IsWeak) {
11353
11355 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11356 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11357}
11358
11362 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11363 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11364
11365 if (!updateToLocation(Loc))
11366 return Loc.IP;
11367
11368 assert(X.Var->getType()->isPointerTy() &&
11369 "OMP atomic expects a pointer to target memory");
11370 // compare capture
11371 if (V.Var) {
11372 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11373 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11374 }
11375
11376 bool IsInteger = E->getType()->isIntegerTy();
11377
11378 if (Op == OMPAtomicCompareOp::EQ) {
11379 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11380 // R.Var handling.
11381 Value *OldValue = nullptr;
11382 Value *SuccessOrFail = nullptr;
11383
11384 if (!IsInteger && HandleFPNegZero) {
11385 // IEEE 754 special cases for cmpxchg (which is bitwise):
11386 // 1. -0.0 == +0.0 but they have different bit patterns.
11387 // 2. NaN != NaN but identical NaN bit patterns would match.
11388 //
11389 // CurBB:
11390 // %e_int = bitcast E to intN
11391 // %d_int = bitcast D to intN
11392 // %x_curr = load atomic intN, X
11393 // %x_fp = bitcast %x_curr to FP
11394 // %e_is_nan = fcmp uno E, E
11395 // %x_is_nan = fcmp uno %x_fp, %x_fp
11396 // %either_nan = or %e_is_nan, %x_is_nan
11397 // br %either_nan, NaNBB, NotNaNBB
11398 // NaNBB: ; NaN == anything is always false
11399 // br ExitBB
11400 // NotNaNBB:
11401 // %x_is_zero = fcmp oeq %x_fp, 0.0
11402 // %e_is_zero = fcmp oeq E, 0.0
11403 // %both_zero = and %x_is_zero, %e_is_zero
11404 // br %both_zero, ZeroBB, NormalBB
11405 // ZeroBB: ; both ±0.0 → x = d
11406 // cmpxchg X, %x_curr, %d_int
11407 // br ExitBB
11408 // NormalBB: ; original path
11409 // cmpxchg X, %e_int, %d_int
11410 // br ExitBB
11411 // ExitBB:
11412 // phi merge
11413 IntegerType *IntCastTy =
11414 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11415 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11416 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11417
11418 // Load X atomically.
11419 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11420 X.Var->getName() + ".atomic.load");
11422 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11423
11424 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11425 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11426 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11427 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11428 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11429
11430 BasicBlock *CurBB = Builder.GetInsertBlock();
11431 Function *F = CurBB->getParent();
11432 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11433 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11434 BasicBlock *ExitBB =
11435 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11437 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11438 BasicBlock *NotNaNBB = BasicBlock::Create(
11439 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11441 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11442 BasicBlock *NormalBB = BasicBlock::Create(
11443 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11444
11445 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11446 CurBB->getTerminator()->eraseFromParent();
11447 Builder.SetInsertPoint(CurBB);
11448 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11449
11450 // NaNBB: NaN == anything is always false; skip cmpxchg.
11451 Builder.SetInsertPoint(NaNBB);
11452 Builder.CreateBr(ExitBB);
11453
11454 // NotNaNBB: check both X and E for ±0.0.
11455 Builder.SetInsertPoint(NotNaNBB);
11456 Value *XIsZero =
11457 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11458 X.Var->getName() + ".atomic.xiszero");
11459 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11460 "atomic.e.iszero");
11461 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11462 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11463
11464 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11465 Builder.SetInsertPoint(ZeroBB);
11466 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11467 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11468 ResZero->setWeak(IsWeak);
11469 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11470 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11471 Builder.CreateBr(ExitBB);
11472
11473 // NormalBB: original bitwise cmpxchg.
11474 Builder.SetInsertPoint(NormalBB);
11475 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11476 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11477 ResNormal->setWeak(IsWeak);
11478 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11479 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11480 Builder.CreateBr(ExitBB);
11481
11482 // ExitBB: merge results from NaN, Zero, and Normal paths.
11483 Builder.SetInsertPoint(ExitBB, ExitBB->begin());
11484 PHINode *OldIntPHI =
11485 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11486 OldIntPHI->addIncoming(XCurr, NaNBB);
11487 OldIntPHI->addIncoming(OldZero, ZeroBB);
11488 OldIntPHI->addIncoming(OldNormal, NormalBB);
11489 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11490 X.Var->getName() + ".atomic.ok");
11491 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11492 SuccessPHI->addIncoming(OkZero, ZeroBB);
11493 SuccessPHI->addIncoming(OkNormal, NormalBB);
11494
11495 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11496 CurBBTI->eraseFromParent();
11497 Builder.SetInsertPoint(ExitBB);
11498 } else {
11499 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11500 }
11501
11502 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11503 X.Var->getName() + ".atomic.old.fp");
11504 SuccessOrFail = SuccessPHI;
11505 } else {
11506 AtomicCmpXchgInst *Result = nullptr;
11507 if (!IsInteger) {
11508 IntegerType *IntCastTy =
11509 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11510 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11511 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11512 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11513 MaybeAlign(), AO, Failure);
11514 } else {
11515 Result =
11516 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11517 }
11518 Result->setWeak(IsWeak);
11519
11520 if (V.Var) {
11521 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11522 if (!IsInteger)
11523 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11524 assert(OldValue->getType() == V.ElemTy &&
11525 "OldValue and V must be of same type");
11526 if (IsPostfixUpdate) {
11527 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11528 } else {
11529 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11530 if (IsFailOnly) {
11531 BasicBlock *CurBB = Builder.GetInsertBlock();
11532 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11533 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11534 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11535 CurBBTI, X.Var->getName() + ".atomic.exit");
11536 BasicBlock *ContBB = CurBB->splitBasicBlock(
11537 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11538 ContBB->getTerminator()->eraseFromParent();
11539 CurBB->getTerminator()->eraseFromParent();
11540
11541 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11542
11543 Builder.SetInsertPoint(ContBB);
11544 Builder.CreateStore(OldValue, V.Var);
11545 Builder.CreateBr(ExitBB);
11546
11547 if (UnreachableInst *ExitTI =
11549 CurBBTI->eraseFromParent();
11550 Builder.SetInsertPoint(ExitBB);
11551 } else {
11552 Builder.SetInsertPoint(ExitTI);
11553 }
11554 } else {
11555 Value *CapturedValue =
11556 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11557 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11558 }
11559 }
11560 }
11561 // The comparison result has to be stored.
11562 if (R.Var) {
11563 assert(R.Var->getType()->isPointerTy() &&
11564 "r.var must be of pointer type");
11565 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11566
11567 Value *SuccessFailureVal =
11568 Builder.CreateExtractValue(Result, /*Idxs=*/1);
11569 Value *ResultCast =
11570 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
11571 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
11572 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11573 }
11574 }
11575
11576 // For the HandleFPNegZero path, handle V.Var and R.Var using the
11577 // pre-computed OldValue and SuccessOrFail.
11578 if (HandleFPNegZero && !IsInteger) {
11579 if (V.Var) {
11580 assert(OldValue->getType() == V.ElemTy &&
11581 "OldValue and V must be of same type");
11582 if (IsPostfixUpdate) {
11583 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11584 } else {
11585 if (IsFailOnly) {
11586 BasicBlock *CurBB = Builder.GetInsertBlock();
11587 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11588 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11589 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11590 CurBBTI, X.Var->getName() + ".atomic.exit");
11591 BasicBlock *ContBB = CurBB->splitBasicBlock(
11592 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11593 ContBB->getTerminator()->eraseFromParent();
11594 CurBB->getTerminator()->eraseFromParent();
11595
11596 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11597
11598 Builder.SetInsertPoint(ContBB);
11599 Builder.CreateStore(OldValue, V.Var);
11600 Builder.CreateBr(ExitBB);
11601
11602 if (UnreachableInst *ExitTI =
11604 CurBBTI->eraseFromParent();
11605 Builder.SetInsertPoint(ExitBB);
11606 } else {
11607 Builder.SetInsertPoint(ExitTI);
11608 }
11609 } else {
11610 Value *CapturedValue =
11611 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11612 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11613 }
11614 }
11615 }
11616 // The comparison result has to be stored.
11617 if (R.Var) {
11618 assert(R.Var->getType()->isPointerTy() &&
11619 "r.var must be of pointer type");
11620 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11621
11622 Value *ResultCast = R.IsSigned
11623 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
11624 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
11625 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11626 }
11627 }
11628 } else {
11629 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
11630 "Op should be either max or min at this point");
11631 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
11632
11633 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
11634 // Let's take max as example.
11635 // OpenMP form:
11636 // x = x > expr ? expr : x;
11637 // LLVM form:
11638 // *ptr = *ptr > val ? *ptr : val;
11639 // We need to transform to LLVM form.
11640 // x = x <= expr ? x : expr;
11642 if (IsXBinopExpr) {
11643 if (IsInteger) {
11644 if (X.IsSigned)
11645 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
11647 else
11648 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
11650 } else {
11651 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
11653 }
11654 } else {
11655 if (IsInteger) {
11656 if (X.IsSigned)
11657 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
11659 else
11660 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
11662 } else {
11663 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
11665 }
11666 }
11667
11668 AtomicRMWInst *OldValue =
11669 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
11670 if (V.Var) {
11671 Value *CapturedValue = nullptr;
11672 if (IsPostfixUpdate) {
11673 CapturedValue = OldValue;
11674 } else {
11675 CmpInst::Predicate Pred;
11676 switch (NewOp) {
11677 case AtomicRMWInst::Max:
11678 Pred = CmpInst::ICMP_SGT;
11679 break;
11681 Pred = CmpInst::ICMP_UGT;
11682 break;
11684 Pred = CmpInst::FCMP_OGT;
11685 break;
11686 case AtomicRMWInst::Min:
11687 Pred = CmpInst::ICMP_SLT;
11688 break;
11690 Pred = CmpInst::ICMP_ULT;
11691 break;
11693 Pred = CmpInst::FCMP_OLT;
11694 break;
11695 default:
11696 llvm_unreachable("unexpected comparison op");
11697 }
11698 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
11699 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
11700 }
11701 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11702 }
11703 }
11704
11705 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
11706
11707 return Builder.saveIP();
11708}
11709
11712 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
11713 Value *NumTeamsUpper, Value *ThreadLimit,
11714 Value *IfExpr) {
11715 if (!updateToLocation(Loc))
11716 return InsertPointTy();
11717
11718 uint32_t SrcLocStrSize;
11719 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
11720 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
11721 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
11722
11723 // Outer allocation basicblock is the entry block of the current function.
11724 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
11725 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
11726 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
11727 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
11728 }
11729
11730 // The current basic block is split into four basic blocks. After outlining,
11731 // they will be mapped as follows:
11732 // ```
11733 // def current_fn() {
11734 // current_basic_block:
11735 // br label %teams.exit
11736 // teams.exit:
11737 // ; instructions after teams
11738 // }
11739 //
11740 // def outlined_fn() {
11741 // teams.alloca:
11742 // br label %teams.body
11743 // teams.body:
11744 // ; instructions within teams body
11745 // }
11746 // ```
11747 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
11748 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
11749 BasicBlock *AllocaBB =
11750 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
11751
11752 bool SubClausesPresent =
11753 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
11754 // Push num_teams
11755 if (!Config.isTargetDevice() && SubClausesPresent) {
11756 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
11757 "if lowerbound is non-null, then upperbound must also be non-null "
11758 "for bounds on num_teams");
11759
11760 if (NumTeamsUpper == nullptr)
11761 NumTeamsUpper = Builder.getInt32(0);
11762
11763 if (NumTeamsLower == nullptr)
11764 NumTeamsLower = NumTeamsUpper;
11765
11766 if (IfExpr) {
11767 assert(IfExpr->getType()->isIntegerTy() &&
11768 "argument to if clause must be an integer value");
11769
11770 // upper = ifexpr ? upper : 1
11771 if (IfExpr->getType() != Int1)
11772 IfExpr = Builder.CreateICmpNE(IfExpr,
11773 ConstantInt::get(IfExpr->getType(), 0));
11774 NumTeamsUpper = Builder.CreateSelect(
11775 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
11776
11777 // lower = ifexpr ? lower : 1
11778 NumTeamsLower = Builder.CreateSelect(
11779 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
11780 }
11781
11782 if (ThreadLimit == nullptr)
11783 ThreadLimit = Builder.getInt32(0);
11784
11785 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
11786 // truncate or sign extend the passed values to match the int32 parameters.
11787 Value *NumTeamsLowerInt32 =
11788 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
11789 Value *NumTeamsUpperInt32 =
11790 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
11791 Value *ThreadLimitInt32 =
11792 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
11793
11794 Value *ThreadNum = getOrCreateThreadID(Ident);
11795
11797 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
11798 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
11799 ThreadLimitInt32});
11800 }
11801 // Generate the body of teams.
11802 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
11803 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
11804 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
11805 return Err;
11806
11807 auto OI = std::make_unique<OutlineInfo>();
11808 OI->EntryBB = AllocaBB;
11809 OI->ExitBB = ExitBB;
11810 OI->OuterAllocBB = &OuterAllocaBB;
11811
11812 // Insert fake values for global tid and bound tid.
11814 InsertPointTy OuterAllocaIP(&OuterAllocaBB, OuterAllocaBB.begin());
11815 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
11816 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
11817 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
11818 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
11819
11820 auto HostPostOutlineCB = [this, Ident,
11821 ToBeDeleted](Function &OutlinedFn) mutable {
11822 // The stale call instruction will be replaced with a new call instruction
11823 // for runtime call with the outlined function.
11824
11825 assert(OutlinedFn.hasOneUse() &&
11826 "there must be a single user for the outlined function");
11827 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
11828 ToBeDeleted.push_back(StaleCI);
11829
11830 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
11831 "Outlined function must have two or three arguments only");
11832
11833 bool HasShared = OutlinedFn.arg_size() == 3;
11834
11835 OutlinedFn.getArg(0)->setName("global.tid.ptr");
11836 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
11837 if (HasShared)
11838 OutlinedFn.getArg(2)->setName("data");
11839
11840 // Call to the runtime function for teams in the current function.
11841 assert(StaleCI && "Error while outlining - no CallInst user found for the "
11842 "outlined function.");
11843 Builder.SetInsertPoint(StaleCI);
11844 SmallVector<Value *> Args = {
11845 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
11846 if (HasShared)
11847 Args.push_back(StaleCI->getArgOperand(2));
11850 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
11851 Args);
11852
11853 for (Instruction *I : llvm::reverse(ToBeDeleted))
11854 I->eraseFromParent();
11855 };
11856
11857 if (!Config.isTargetDevice())
11858 OI->PostOutlineCB = HostPostOutlineCB;
11859
11860 addOutlineInfo(std::move(OI));
11861
11862 Builder.SetInsertPoint(ExitBB);
11863
11864 return Builder.saveIP();
11865}
11866
11868 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
11869 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
11870 if (!updateToLocation(Loc))
11871 return InsertPointTy();
11872
11873 BasicBlock *OuterAllocaBB = OuterAllocIP.getBlock();
11874
11875 if (OuterAllocaBB == Builder.GetInsertBlock()) {
11876 BasicBlock *BodyBB =
11877 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
11878 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
11879 }
11880 BasicBlock *ExitBB =
11881 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
11882 BasicBlock *BodyBB =
11883 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
11884 BasicBlock *AllocaBB =
11885 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
11886
11887 // Generate the body of distribute clause
11888 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
11889 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
11890 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
11891 return Err;
11892
11893 // When using target we use different runtime functions which require a
11894 // callback.
11895 if (Config.isTargetDevice()) {
11896 auto OI = std::make_unique<OutlineInfo>();
11897 OI->OuterAllocBB = OuterAllocIP.getBlock();
11898 OI->EntryBB = AllocaBB;
11899 OI->ExitBB = ExitBB;
11900 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
11901 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
11902
11903 addOutlineInfo(std::move(OI));
11904 }
11905 Builder.SetInsertPoint(ExitBB);
11906
11907 return Builder.saveIP();
11908}
11909
11912 std::string VarName) {
11913 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
11915 Names.size()),
11916 Names);
11917 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
11918 M, MapNamesArrayInit->getType(),
11919 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
11920 VarName);
11921 return MapNamesArrayGlobal;
11922}
11923
11924// Create all simple and struct types exposed by the runtime and remember
11925// the llvm::PointerTypes of them for easy access later.
11926void OpenMPIRBuilder::initializeTypes(Module &M) {
11927 LLVMContext &Ctx = M.getContext();
11928 StructType *T;
11929 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
11930 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
11931#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
11932#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
11933 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
11934 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
11935#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
11936 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
11937 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
11938#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
11939 T = StructType::getTypeByName(Ctx, StructName); \
11940 if (!T) \
11941 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
11942 VarName = T; \
11943 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
11944#include "llvm/Frontend/OpenMP/OMPKinds.def"
11945}
11946
11949 SmallVectorImpl<BasicBlock *> &BlockVector) {
11951 BlockSet.insert(EntryBB);
11952 BlockSet.insert(ExitBB);
11953
11954 Worklist.push_back(EntryBB);
11955 while (!Worklist.empty()) {
11956 BasicBlock *BB = Worklist.pop_back_val();
11957 BlockVector.push_back(BB);
11958 for (BasicBlock *SuccBB : successors(BB))
11959 if (BlockSet.insert(SuccBB).second)
11960 Worklist.push_back(SuccBB);
11961 }
11962}
11963
11964std::unique_ptr<CodeExtractor>
11966 bool ArgsInZeroAddressSpace,
11967 Twine Suffix) {
11968 return std::make_unique<CodeExtractor>(
11969 Blocks, /* DominatorTree */ nullptr,
11970 /* AggregateArgs */ true,
11971 /* BlockFrequencyInfo */ nullptr,
11972 /* BranchProbabilityInfo */ nullptr,
11973 /* AssumptionCache */ nullptr,
11974 /* AllowVarArgs */ true,
11975 /* AllowAlloca */ true,
11976 /* AllocationBlock*/ OuterAllocBB,
11977 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
11978 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
11979}
11980
11981std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
11982 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
11983 return std::make_unique<DeviceSharedMemCodeExtractor>(
11984 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
11985 /* AggregateArgs */ true,
11986 /* BlockFrequencyInfo */ nullptr,
11987 /* BranchProbabilityInfo */ nullptr,
11988 /* AssumptionCache */ nullptr,
11989 /* AllowVarArgs */ true,
11990 /* AllowAlloca */ true,
11991 /* AllocationBlock*/ OuterAllocBB,
11992 /* DeallocationBlocks */ OuterDeallocBBs.empty()
11994 : OuterDeallocBBs,
11995 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
11996}
11997
11999 uint64_t Size, int32_t Flags,
12001 StringRef Name) {
12002 if (!Config.isGPU()) {
12005 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12006 return;
12007 }
12008 // TODO: Add support for global variables on the device after declare target
12009 // support.
12010 Function *Fn = dyn_cast<Function>(Addr);
12011 if (!Fn)
12012 return;
12013
12014 // Add a function attribute for the kernel.
12015 Fn->addFnAttr("kernel");
12016 if (T.isAMDGCN())
12017 Fn->addFnAttr("uniform-work-group-size");
12018 Fn->addFnAttr(Attribute::MustProgress);
12019}
12020
12021// We only generate metadata for function that contain target regions.
12024
12025 // If there are no entries, we don't need to do anything.
12026 if (OffloadInfoManager.empty())
12027 return;
12028
12029 LLVMContext &C = M.getContext();
12032 16>
12033 OrderedEntries(OffloadInfoManager.size());
12034
12035 // Auxiliary methods to create metadata values and strings.
12036 auto &&GetMDInt = [this](unsigned V) {
12037 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12038 };
12039
12040 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12041
12042 // Create the offloading info metadata node.
12043 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12044 auto &&TargetRegionMetadataEmitter =
12045 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12046 const TargetRegionEntryInfo &EntryInfo,
12048 // Generate metadata for target regions. Each entry of this metadata
12049 // contains:
12050 // - Entry 0 -> Kind of this type of metadata (0).
12051 // - Entry 1 -> Device ID of the file where the entry was identified.
12052 // - Entry 2 -> File ID of the file where the entry was identified.
12053 // - Entry 3 -> Mangled name of the function where the entry was
12054 // identified.
12055 // - Entry 4 -> Line in the file where the entry was identified.
12056 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12057 // - Entry 6 -> Order the entry was created.
12058 // The first element of the metadata node is the kind.
12059 Metadata *Ops[] = {
12060 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12061 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12062 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12063 GetMDInt(E.getOrder())};
12064
12065 // Save this entry in the right position of the ordered entries array.
12066 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12067
12068 // Add metadata to the named metadata node.
12069 MD->addOperand(MDNode::get(C, Ops));
12070 };
12071
12072 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12073
12074 // Create function that emits metadata for each device global variable entry;
12075 auto &&DeviceGlobalVarMetadataEmitter =
12076 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12077 StringRef MangledName,
12079 // Generate metadata for global variables. Each entry of this metadata
12080 // contains:
12081 // - Entry 0 -> Kind of this type of metadata (1).
12082 // - Entry 1 -> Mangled name of the variable.
12083 // - Entry 2 -> Declare target kind.
12084 // - Entry 3 -> Order the entry was created.
12085 // The first element of the metadata node is the kind.
12086 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12087 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12088
12089 // Save this entry in the right position of the ordered entries array.
12090 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12091 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12092
12093 // Add metadata to the named metadata node.
12094 MD->addOperand(MDNode::get(C, Ops));
12095 };
12096
12097 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12098 DeviceGlobalVarMetadataEmitter);
12099
12100 for (const auto &E : OrderedEntries) {
12101 assert(E.first && "All ordered entries must exist!");
12102 if (const auto *CE =
12104 E.first)) {
12105 if (!CE->getID() || !CE->getAddress()) {
12106 // Do not blame the entry if the parent funtion is not emitted.
12107 TargetRegionEntryInfo EntryInfo = E.second;
12108 StringRef FnName = EntryInfo.ParentName;
12109 if (!M.getNamedValue(FnName))
12110 continue;
12111 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12112 continue;
12113 }
12114 createOffloadEntry(CE->getID(), CE->getAddress(),
12115 /*Size=*/0, CE->getFlags(),
12117 } else if (const auto *CE = dyn_cast<
12119 E.first)) {
12122 CE->getFlags());
12123 switch (Flags) {
12126 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12127 continue;
12128 if (!CE->getAddress()) {
12129 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12130 continue;
12131 }
12132 // The vaiable has no definition - no need to add the entry.
12133 if (CE->getVarSize() == 0)
12134 continue;
12135 break;
12137 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12138 (!Config.isTargetDevice() && CE->getAddress())) &&
12139 "Declaret target link address is set.");
12140 if (Config.isTargetDevice())
12141 continue;
12142 if (!CE->getAddress()) {
12144 continue;
12145 }
12146 break;
12149 if (!CE->getAddress()) {
12150 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12151 continue;
12152 }
12153 break;
12154 default:
12155 break;
12156 }
12157
12158 // Hidden or internal symbols on the device are not externally visible.
12159 // We should not attempt to register them by creating an offloading
12160 // entry. Indirect variables are handled separately on the device.
12161 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12162 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12163 (Flags !=
12165 Flags != OffloadEntriesInfoManager::
12166 OMPTargetGlobalVarEntryIndirectVTable))
12167 continue;
12168
12169 // Indirect globals need to use a special name that doesn't match the name
12170 // of the associated host global.
12172 Flags ==
12174 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12175 Flags, CE->getLinkage(), CE->getVarName());
12176 else
12177 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12178 Flags, CE->getLinkage());
12179
12180 } else {
12181 llvm_unreachable("Unsupported entry kind.");
12182 }
12183 }
12184
12185 // Emit requires directive globals to a special entry so the runtime can
12186 // register them when the device image is loaded.
12187 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12188 // entries should be redesigned to better suit this use-case.
12189 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12193 ".requires", /*Size=*/0,
12195 Config.getRequiresFlags());
12196}
12197
12200 unsigned FileID, unsigned Line, unsigned Count) {
12201 raw_svector_ostream OS(Name);
12202 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12203 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12204 if (Count)
12205 OS << "_" << Count;
12206}
12207
12209 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12210 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12212 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12213 EntryInfo.Line, NewCount);
12214}
12215
12218 vfs::FileSystem &VFS,
12219 StringRef ParentName) {
12220 sys::fs::UniqueID ID(0xdeadf17e, 0);
12221 auto FileIDInfo = CallBack();
12222 uint64_t FileID = 0;
12223 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12224 ID = Status->getUniqueID();
12225 FileID = Status->getUniqueID().getFile();
12226 } else {
12227 // If the inode ID could not be determined, create a hash value
12228 // the current file name and use that as an ID.
12229 FileID = hash_value(std::get<0>(FileIDInfo));
12230 }
12231
12232 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12233 std::get<1>(FileIDInfo));
12234}
12235
12237 unsigned Offset = 0;
12238 for (uint64_t Remain =
12239 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12241 !(Remain & 1); Remain = Remain >> 1)
12242 Offset++;
12243 return Offset;
12244}
12245
12248 // Rotate by getFlagMemberOffset() bits.
12249 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12250 << getFlagMemberOffset());
12251}
12252
12255 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12256 // If the entry is PTR_AND_OBJ but has not been marked with the special
12257 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12258 // marked as MEMBER_OF.
12259 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12261 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12264 return;
12265
12266 // Entries with ATTACH are not members-of anything. They are handled
12267 // separately by the runtime after other maps have been handled.
12268 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12270 return;
12271
12272 // Reset the placeholder value to prepare the flag for the assignment of the
12273 // proper MEMBER_OF value.
12274 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12275 Flags |= MemberOfFlag;
12276}
12277
12281 bool IsDeclaration, bool IsExternallyVisible,
12282 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12283 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12284 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12285 std::function<Constant *()> GlobalInitializer,
12286 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12287 // TODO: convert this to utilise the IRBuilder Config rather than
12288 // a passed down argument.
12289 if (OpenMPSIMD)
12290 return nullptr;
12291
12294 CaptureClause ==
12296 Config.hasRequiresUnifiedSharedMemory())) {
12297 SmallString<64> PtrName;
12298 {
12299 raw_svector_ostream OS(PtrName);
12300 OS << MangledName;
12301 if (!IsExternallyVisible)
12302 OS << format("_%x", EntryInfo.FileID);
12303 OS << "_decl_tgt_ref_ptr";
12304 }
12305
12306 Value *Ptr = M.getNamedValue(PtrName);
12307
12308 if (!Ptr) {
12309 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12310 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12311
12312 auto *GV = cast<GlobalVariable>(Ptr);
12313 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12314
12315 if (!Config.isTargetDevice()) {
12316 if (GlobalInitializer)
12317 GV->setInitializer(GlobalInitializer());
12318 else
12319 GV->setInitializer(GlobalValue);
12320 }
12321
12323 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12324 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12325 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12326 }
12327
12328 return cast<Constant>(Ptr);
12329 }
12330
12331 return nullptr;
12332}
12333
12337 bool IsDeclaration, bool IsExternallyVisible,
12338 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12339 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12340 std::vector<Triple> TargetTriple,
12341 std::function<Constant *()> GlobalInitializer,
12342 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12343 Constant *Addr) {
12345 (TargetTriple.empty() && !Config.isTargetDevice()))
12346 return;
12347
12349 StringRef VarName;
12350 int64_t VarSize;
12352
12354 CaptureClause ==
12356 !Config.hasRequiresUnifiedSharedMemory()) {
12358 VarName = MangledName;
12359 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12360
12361 if (!IsDeclaration)
12362 VarSize = divideCeil(
12363 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12364 else
12365 VarSize = 0;
12366 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12367
12368 // This is a workaround carried over from Clang which prevents undesired
12369 // optimisation of internal variables.
12370 if (Config.isTargetDevice() &&
12371 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12372 // Do not create a "ref-variable" if the original is not also available
12373 // on the host.
12374 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12375 return;
12376
12377 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12378
12379 if (!M.getNamedValue(RefName)) {
12380 Constant *AddrRef =
12381 getOrCreateInternalVariable(Addr->getType(), RefName);
12382 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12383 GvAddrRef->setConstant(true);
12384 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12385 GvAddrRef->setInitializer(Addr);
12386 GeneratedRefs.push_back(GvAddrRef);
12387 }
12388 }
12389 } else {
12392 else
12394
12395 if (Config.isTargetDevice()) {
12396 VarName = (Addr) ? Addr->getName() : "";
12397 Addr = nullptr;
12398 } else {
12400 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12401 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12402 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12403 VarName = (Addr) ? Addr->getName() : "";
12404 }
12405 VarSize = M.getDataLayout().getPointerSize();
12407 }
12408
12409 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12410 Flags, Linkage);
12411}
12412
12413/// Loads all the offload entries information from the host IR
12414/// metadata.
12416 // If we are in target mode, load the metadata from the host IR. This code has
12417 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12418
12419 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12420 if (!MD)
12421 return;
12422
12423 for (MDNode *MN : MD->operands()) {
12424 auto &&GetMDInt = [MN](unsigned Idx) {
12425 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12426 return cast<ConstantInt>(V->getValue())->getZExtValue();
12427 };
12428
12429 auto &&GetMDString = [MN](unsigned Idx) {
12430 auto *V = cast<MDString>(MN->getOperand(Idx));
12431 return V->getString();
12432 };
12433
12434 switch (GetMDInt(0)) {
12435 default:
12436 llvm_unreachable("Unexpected metadata!");
12437 break;
12438 case OffloadEntriesInfoManager::OffloadEntryInfo::
12439 OffloadingEntryInfoTargetRegion: {
12440 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12441 /*DeviceID=*/GetMDInt(1),
12442 /*FileID=*/GetMDInt(2),
12443 /*Line=*/GetMDInt(4),
12444 /*Count=*/GetMDInt(5));
12445 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12446 /*Order=*/GetMDInt(6));
12447 break;
12448 }
12449 case OffloadEntriesInfoManager::OffloadEntryInfo::
12450 OffloadingEntryInfoDeviceGlobalVar:
12451 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12452 /*MangledName=*/GetMDString(1),
12454 /*Flags=*/GetMDInt(2)),
12455 /*Order=*/GetMDInt(3));
12456 break;
12457 }
12458 }
12459}
12460
12462 StringRef HostFilePath) {
12463 if (HostFilePath.empty())
12464 return;
12465
12466 auto Buf = VFS.getBufferForFile(HostFilePath);
12467 if (std::error_code Err = Buf.getError()) {
12468 report_fatal_error(("error opening host file from host file path inside of "
12469 "OpenMPIRBuilder: " +
12470 Err.message())
12471 .c_str());
12472 }
12473
12474 LLVMContext Ctx;
12476 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12477 if (std::error_code Err = M.getError()) {
12479 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12480 .c_str());
12481 }
12482
12483 loadOffloadInfoMetadata(*M.get());
12484}
12485
12488 llvm::StringRef Name) {
12489 Builder.restoreIP(Loc.IP);
12490
12491 BasicBlock *CurBB = Builder.GetInsertBlock();
12492 assert(CurBB &&
12493 "expected a valid insertion block for creating an iterator loop");
12494 Function *F = CurBB->getParent();
12495
12496 InsertPointTy SplitIP = Builder.saveIP();
12497 if (SplitIP.getPoint() == CurBB->end())
12498 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12499 SplitIP = InsertPointTy(CurBB, Terminator->getIterator());
12500
12501 BasicBlock *ContBB =
12502 splitBB(SplitIP, /*CreateBranch=*/false,
12503 Builder.getCurrentDebugLocation(), "omp.it.cont");
12504
12505 CanonicalLoopInfo *CLI =
12506 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12507 /*PreInsertBefore=*/ContBB,
12508 /*PostInsertBefore=*/ContBB, Name);
12509
12510 // Enter loop from original block.
12511 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12512
12513 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12514 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12515 T->eraseFromParent();
12516
12517 InsertPointTy BodyIP = CLI->getBodyIP();
12518 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12519 return Err;
12520
12521 // Body must either fallthrough to the latch or branch directly to it.
12522 if (Instruction *BodyTerminator = CLI->getBody()->getTerminatorOrNull()) {
12523 auto *BodyBr = dyn_cast<UncondBrInst>(BodyTerminator);
12524 if (!BodyBr || BodyBr->getSuccessor() != CLI->getLatch()) {
12526 "iterator bodygen must terminate the canonical body with an "
12527 "unconditional branch to the loop latch",
12529 }
12530 } else {
12531 // Ensure we end the loop body by jumping to the latch.
12532 Builder.SetInsertPoint(CLI->getBody());
12533 Builder.CreateBr(CLI->getLatch());
12534 }
12535
12536 // Link After -> ContBB
12537 Builder.SetInsertPoint(CLI->getAfter(), CLI->getAfter()->begin());
12538 if (!CLI->getAfter()->hasTerminator())
12539 Builder.CreateBr(ContBB);
12540
12541 return InsertPointTy{ContBB, ContBB->begin()};
12542}
12543
12544/// Mangle the parameter part of the vector function name according to
12545/// their OpenMP classification. The mangling function is defined in
12546/// section 4.5 of the AAVFABI(2021Q1).
12547static std::string mangleVectorParameters(
12549 SmallString<256> Buffer;
12550 llvm::raw_svector_ostream Out(Buffer);
12551 for (const auto &ParamAttr : ParamAttrs) {
12552 switch (ParamAttr.Kind) {
12554 Out << 'l';
12555 break;
12557 Out << 'R';
12558 break;
12560 Out << 'U';
12561 break;
12563 Out << 'L';
12564 break;
12566 Out << 'u';
12567 break;
12569 Out << 'v';
12570 break;
12571 }
12572 if (ParamAttr.HasVarStride)
12573 Out << "s" << ParamAttr.StrideOrArg;
12574 else if (ParamAttr.Kind ==
12576 ParamAttr.Kind ==
12578 ParamAttr.Kind ==
12580 ParamAttr.Kind ==
12582 // Don't print the step value if it is not present or if it is
12583 // equal to 1.
12584 if (ParamAttr.StrideOrArg < 0)
12585 Out << 'n' << -ParamAttr.StrideOrArg;
12586 else if (ParamAttr.StrideOrArg != 1)
12587 Out << ParamAttr.StrideOrArg;
12588 }
12589
12590 if (!!ParamAttr.Alignment)
12591 Out << 'a' << ParamAttr.Alignment;
12592 }
12593
12594 return std::string(Out.str());
12595}
12596
12598 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
12600 struct ISADataTy {
12601 char ISA;
12602 unsigned VecRegSize;
12603 };
12604 ISADataTy ISAData[] = {
12605 {'b', 128}, // SSE
12606 {'c', 256}, // AVX
12607 {'d', 256}, // AVX2
12608 {'e', 512}, // AVX512
12609 };
12611 switch (Branch) {
12613 Masked.push_back('N');
12614 Masked.push_back('M');
12615 break;
12617 Masked.push_back('N');
12618 break;
12620 Masked.push_back('M');
12621 break;
12622 }
12623 for (char Mask : Masked) {
12624 for (const ISADataTy &Data : ISAData) {
12626 llvm::raw_svector_ostream Out(Buffer);
12627 Out << "_ZGV" << Data.ISA << Mask;
12628 if (!VLENVal) {
12629 assert(NumElts && "Non-zero simdlen/cdtsize expected");
12630 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
12631 } else {
12632 Out << VLENVal;
12633 }
12634 Out << mangleVectorParameters(ParamAttrs);
12635 Out << '_' << Fn->getName();
12636 Fn->addFnAttr(Out.str());
12637 }
12638 }
12639}
12640
12641// Function used to add the attribute. The parameter `VLEN` is templated to
12642// allow the use of `x` when targeting scalable functions for SVE.
12643template <typename T>
12644static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
12645 char ISA, StringRef ParSeq,
12646 StringRef MangledName, bool OutputBecomesInput,
12647 llvm::Function *Fn) {
12648 SmallString<256> Buffer;
12649 llvm::raw_svector_ostream Out(Buffer);
12650 Out << Prefix << ISA << LMask << VLEN;
12651 if (OutputBecomesInput)
12652 Out << 'v';
12653 Out << ParSeq << '_' << MangledName;
12654 Fn->addFnAttr(Out.str());
12655}
12656
12657// Helper function to generate the Advanced SIMD names depending on the value
12658// of the NDS when simdlen is not present.
12659static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
12660 StringRef Prefix, char ISA,
12661 StringRef ParSeq, StringRef MangledName,
12662 bool OutputBecomesInput,
12663 llvm::Function *Fn) {
12664 switch (NDS) {
12665 case 8:
12666 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12667 OutputBecomesInput, Fn);
12668 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
12669 OutputBecomesInput, Fn);
12670 break;
12671 case 16:
12672 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
12673 OutputBecomesInput, Fn);
12674 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12675 OutputBecomesInput, Fn);
12676 break;
12677 case 32:
12678 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
12679 OutputBecomesInput, Fn);
12680 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
12681 OutputBecomesInput, Fn);
12682 break;
12683 case 64:
12684 case 128:
12685 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
12686 OutputBecomesInput, Fn);
12687 break;
12688 default:
12689 llvm_unreachable("Scalar type is too wide.");
12690 }
12691}
12692
12693/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
12695 llvm::Function *Fn, unsigned UserVLEN,
12697 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
12698 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
12699
12700 // Sort out parameter sequence.
12701 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
12702 StringRef Prefix = "_ZGV";
12703 StringRef MangledName = Fn->getName();
12704
12705 // Generate simdlen from user input (if any).
12706 if (UserVLEN) {
12707 if (ISA == 's') {
12708 // SVE generates only a masked function.
12709 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
12710 OutputBecomesInput, Fn);
12711 return;
12712 }
12713
12714 switch (Branch) {
12716 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
12717 OutputBecomesInput, Fn);
12718 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
12719 OutputBecomesInput, Fn);
12720 break;
12722 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
12723 OutputBecomesInput, Fn);
12724 break;
12726 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
12727 OutputBecomesInput, Fn);
12728 break;
12729 }
12730 return;
12731 }
12732
12733 if (ISA == 's') {
12734 // SVE, section 3.4.1, item 1.
12735 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
12736 OutputBecomesInput, Fn);
12737 return;
12738 }
12739
12740 switch (Branch) {
12742 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
12743 MangledName, OutputBecomesInput, Fn);
12744 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
12745 MangledName, OutputBecomesInput, Fn);
12746 break;
12748 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
12749 MangledName, OutputBecomesInput, Fn);
12750 break;
12752 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
12753 MangledName, OutputBecomesInput, Fn);
12754 break;
12755 }
12756}
12757
12758//===----------------------------------------------------------------------===//
12759// OffloadEntriesInfoManager
12760//===----------------------------------------------------------------------===//
12761
12763 return OffloadEntriesTargetRegion.empty() &&
12764 OffloadEntriesDeviceGlobalVar.empty();
12765}
12766
12767unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
12768 const TargetRegionEntryInfo &EntryInfo) const {
12769 auto It = OffloadEntriesTargetRegionCount.find(
12770 getTargetRegionEntryCountKey(EntryInfo));
12771 if (It == OffloadEntriesTargetRegionCount.end())
12772 return 0;
12773 return It->second;
12774}
12775
12776void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
12777 const TargetRegionEntryInfo &EntryInfo) {
12778 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
12779 EntryInfo.Count + 1;
12780}
12781
12782/// Initialize target region entry.
12784 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
12785 OffloadEntriesTargetRegion[EntryInfo] =
12786 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
12788 ++OffloadingEntriesNum;
12789}
12790
12792 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
12794 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
12795
12796 // Update the EntryInfo with the next available count for this location.
12797 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
12798
12799 // If we are emitting code for a target, the entry is already initialized,
12800 // only has to be registered.
12801 if (OMPBuilder->Config.isTargetDevice()) {
12802 // This could happen if the device compilation is invoked standalone.
12803 if (!hasTargetRegionEntryInfo(EntryInfo)) {
12804 return;
12805 }
12806 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
12807 Entry.setAddress(Addr);
12808 Entry.setID(ID);
12809 Entry.setFlags(Flags);
12810 } else {
12812 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
12813 return;
12814 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
12815 "Target region entry already registered!");
12816 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
12817 OffloadEntriesTargetRegion[EntryInfo] = Entry;
12818 ++OffloadingEntriesNum;
12819 }
12820 incrementTargetRegionEntryInfoCount(EntryInfo);
12821}
12822
12824 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
12825
12826 // Update the EntryInfo with the next available count for this location.
12827 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
12828
12829 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
12830 if (It == OffloadEntriesTargetRegion.end()) {
12831 return false;
12832 }
12833 // Fail if this entry is already registered.
12834 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
12835 return false;
12836 return true;
12837}
12838
12840 const OffloadTargetRegionEntryInfoActTy &Action) {
12841 // Scan all target region entries and perform the provided action.
12842 for (const auto &It : OffloadEntriesTargetRegion) {
12843 Action(It.first, It.second);
12844 }
12845}
12846
12848 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
12849 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
12850 ++OffloadingEntriesNum;
12851}
12852
12854 StringRef VarName, Constant *Addr, int64_t VarSize,
12856 if (OMPBuilder->Config.isTargetDevice()) {
12857 // This could happen if the device compilation is invoked standalone.
12858 if (!hasDeviceGlobalVarEntryInfo(VarName))
12859 return;
12860 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
12861 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
12862 if (Entry.getVarSize() == 0) {
12863 Entry.setVarSize(VarSize);
12864 Entry.setLinkage(Linkage);
12865 }
12866 return;
12867 }
12868 Entry.setVarSize(VarSize);
12869 Entry.setLinkage(Linkage);
12870 Entry.setAddress(Addr);
12871 } else {
12872 if (hasDeviceGlobalVarEntryInfo(VarName)) {
12873 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
12874 assert(Entry.isValid() && Entry.getFlags() == Flags &&
12875 "Entry not initialized!");
12876 if (Entry.getVarSize() == 0) {
12877 Entry.setVarSize(VarSize);
12878 Entry.setLinkage(Linkage);
12879 }
12880 return;
12881 }
12883 Flags ==
12885 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
12886 Addr, VarSize, Flags, Linkage,
12887 VarName.str());
12888 else
12889 OffloadEntriesDeviceGlobalVar.try_emplace(
12890 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
12891 ++OffloadingEntriesNum;
12892 }
12893}
12894
12897 // Scan all target region entries and perform the provided action.
12898 for (const auto &E : OffloadEntriesDeviceGlobalVar)
12899 Action(E.getKey(), E.getValue());
12900}
12901
12902//===----------------------------------------------------------------------===//
12903// CanonicalLoopInfo
12904//===----------------------------------------------------------------------===//
12905
12906void CanonicalLoopInfo::collectControlBlocks(
12908 // We only count those BBs as control block for which we do not need to
12909 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
12910 // flow. For consistency, this also means we do not add the Body block, which
12911 // is just the entry to the body code.
12912 BBs.reserve(BBs.size() + 6);
12913 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
12914}
12915
12917 assert(isValid() && "Requires a valid canonical loop");
12918 for (BasicBlock *Pred : predecessors(Header)) {
12919 if (Pred != Latch)
12920 return Pred;
12921 }
12922 llvm_unreachable("Missing preheader");
12923}
12924
12925void CanonicalLoopInfo::setTripCount(Value *TripCount) {
12926 assert(isValid() && "Requires a valid canonical loop");
12927
12928 Instruction *CmpI = &getCond()->front();
12929 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
12930 CmpI->setOperand(1, TripCount);
12931
12932#ifndef NDEBUG
12933 assertOK();
12934#endif
12935}
12936
12937void CanonicalLoopInfo::mapIndVar(
12938 llvm::function_ref<Value *(Instruction *)> Updater) {
12939 assert(isValid() && "Requires a valid canonical loop");
12940
12941 Instruction *OldIV = getIndVar();
12942
12943 // Record all uses excluding those introduced by the updater. Uses by the
12944 // CanonicalLoopInfo itself to keep track of the number of iterations are
12945 // excluded.
12946 SmallVector<Use *> ReplacableUses;
12947 for (Use &U : OldIV->uses()) {
12948 auto *User = dyn_cast<Instruction>(U.getUser());
12949 if (!User)
12950 continue;
12951 if (User->getParent() == getCond())
12952 continue;
12953 if (User->getParent() == getLatch())
12954 continue;
12955 ReplacableUses.push_back(&U);
12956 }
12957
12958 // Run the updater that may introduce new uses
12959 Value *NewIV = Updater(OldIV);
12960
12961 // Replace the old uses with the value returned by the updater.
12962 for (Use *U : ReplacableUses)
12963 U->set(NewIV);
12964
12965#ifndef NDEBUG
12966 assertOK();
12967#endif
12968}
12969
12971#ifndef NDEBUG
12972 // No constraints if this object currently does not describe a loop.
12973 if (!isValid())
12974 return;
12975
12976 BasicBlock *Preheader = getPreheader();
12977 BasicBlock *Body = getBody();
12978 BasicBlock *After = getAfter();
12979
12980 // Verify standard control-flow we use for OpenMP loops.
12981 assert(Preheader);
12982 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
12983 "Preheader must terminate with unconditional branch");
12984 assert(Preheader->getSingleSuccessor() == Header &&
12985 "Preheader must jump to header");
12986
12987 assert(Header);
12988 assert(isa<UncondBrInst>(Header->getTerminator()) &&
12989 "Header must terminate with unconditional branch");
12990 assert(Header->getSingleSuccessor() == Cond &&
12991 "Header must jump to exiting block");
12992
12993 assert(Cond);
12994 assert(Cond->getSinglePredecessor() == Header &&
12995 "Exiting block only reachable from header");
12996
12997 assert(isa<CondBrInst>(Cond->getTerminator()) &&
12998 "Exiting block must terminate with conditional branch");
12999 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13000 "Exiting block's first successor jump to the body");
13001 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13002 "Exiting block's second successor must exit the loop");
13003
13004 assert(Body);
13005 assert(Body->getSinglePredecessor() == Cond &&
13006 "Body only reachable from exiting block");
13007 assert(!isa<PHINode>(Body->front()));
13008
13009 assert(Latch);
13010 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13011 "Latch must terminate with unconditional branch");
13012 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13013 // TODO: To support simple redirecting of the end of the body code that has
13014 // multiple; introduce another auxiliary basic block like preheader and after.
13015 assert(Latch->getSinglePredecessor() != nullptr);
13016 assert(!isa<PHINode>(Latch->front()));
13017
13018 assert(Exit);
13019 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13020 "Exit block must terminate with unconditional branch");
13021 assert(Exit->getSingleSuccessor() == After &&
13022 "Exit block must jump to after block");
13023
13024 assert(After);
13025 assert(After->getSinglePredecessor() == Exit &&
13026 "After block only reachable from exit block");
13027 assert(After->empty() || !isa<PHINode>(After->front()));
13028
13029 Instruction *IndVar = getIndVar();
13030 assert(IndVar && "Canonical induction variable not found?");
13031 assert(isa<IntegerType>(IndVar->getType()) &&
13032 "Induction variable must be an integer");
13033 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13034 "Induction variable must be a PHI in the loop header");
13035 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13036 assert(
13037 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13038 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13039
13040 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13041 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13042 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13043 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13044 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13045 ->isOne());
13046
13047 Value *TripCount = getTripCount();
13048 assert(TripCount && "Loop trip count not found?");
13049 assert(IndVar->getType() == TripCount->getType() &&
13050 "Trip count and induction variable must have the same type");
13051
13052 auto *CmpI = cast<CmpInst>(&Cond->front());
13053 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13054 "Exit condition must be a signed less-than comparison");
13055 assert(CmpI->getOperand(0) == IndVar &&
13056 "Exit condition must compare the induction variable");
13057 assert(CmpI->getOperand(1) == TripCount &&
13058 "Exit condition must compare with the trip count");
13059#endif
13060}
13061
13063 Header = nullptr;
13064 Cond = nullptr;
13065 Latch = nullptr;
13066 Exit = nullptr;
13067}
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< 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 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 wrapper over IRBuilderBase::restoreIP that also restores the current debug location to the la...
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 LLVM_ABI ConstantInt * getTrue(LLVMContext &Context)
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:270
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:151
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:633
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:440
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:758
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:661
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:1573
ArrayRef< MDOperand > operands() const
Definition Metadata.h:1424
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1565
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Definition Metadata.cpp:614
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:1753
iterator_range< op_iterator > operands()
Definition Metadata.h:1849
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 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.
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(Type *ElementType)
This constructs a pointer to an object of the specified type in the default address space (address sp...
static LLVM_ABI PointerType * get(Type *ElementType, unsigned AddressSpace)
This constructs a pointer to an object of the specified type in a numbered address space.
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:230
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
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
static LLVM_ABI IntegerType * getInt1Ty(LLVMContext &C)
Definition Type.cpp:306
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
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.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
@ 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.
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
@ 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:82
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),...