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"
65
66#include <cstdint>
67#include <optional>
68
69#define DEBUG_TYPE "openmp-ir-builder"
70
71using namespace llvm;
72using namespace omp;
73
74static cl::opt<bool>
75 OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden,
76 cl::desc("Use optimistic attributes describing "
77 "'as-if' properties of runtime calls."),
78 cl::init(false));
79
81 "openmp-ir-builder-unroll-threshold-factor", cl::Hidden,
82 cl::desc("Factor for the unroll threshold to account for code "
83 "simplifications still taking place"),
84 cl::init(1.5));
85
87 "openmp-ir-builder-use-default-max-threads", cl::Hidden,
88 cl::desc("Use a default max threads if none is provided."), cl::init(true));
89
90#ifndef NDEBUG
91/// Return whether IP1 and IP2 are ambiguous, i.e. that inserting instructions
92/// at position IP1 may change the meaning of IP2 or vice-versa. This is because
93/// an InsertPoint stores the instruction before something is inserted. For
94/// instance, if both point to the same instruction, two IRBuilders alternating
95/// creating instruction will cause the instructions to be interleaved.
98 if (!IP1.isSet() || !IP2.isSet())
99 return false;
100 return IP1.getBlock() == IP2.getBlock() && IP1.getPoint() == IP2.getPoint();
101}
102
104 // Valid ordered/unordered and base algorithm combinations.
105 switch (SchedType & ~OMPScheduleType::MonotonicityMask) {
106 case OMPScheduleType::UnorderedStaticChunked:
107 case OMPScheduleType::UnorderedStatic:
108 case OMPScheduleType::UnorderedDynamicChunked:
109 case OMPScheduleType::UnorderedGuidedChunked:
110 case OMPScheduleType::UnorderedRuntime:
111 case OMPScheduleType::UnorderedAuto:
112 case OMPScheduleType::UnorderedTrapezoidal:
113 case OMPScheduleType::UnorderedGreedy:
114 case OMPScheduleType::UnorderedBalanced:
115 case OMPScheduleType::UnorderedGuidedIterativeChunked:
116 case OMPScheduleType::UnorderedGuidedAnalyticalChunked:
117 case OMPScheduleType::UnorderedSteal:
118 case OMPScheduleType::UnorderedStaticBalancedChunked:
119 case OMPScheduleType::UnorderedGuidedSimd:
120 case OMPScheduleType::UnorderedRuntimeSimd:
121 case OMPScheduleType::OrderedStaticChunked:
122 case OMPScheduleType::OrderedStatic:
123 case OMPScheduleType::OrderedDynamicChunked:
124 case OMPScheduleType::OrderedGuidedChunked:
125 case OMPScheduleType::OrderedRuntime:
126 case OMPScheduleType::OrderedAuto:
127 case OMPScheduleType::OrderdTrapezoidal:
128 case OMPScheduleType::NomergeUnorderedStaticChunked:
129 case OMPScheduleType::NomergeUnorderedStatic:
130 case OMPScheduleType::NomergeUnorderedDynamicChunked:
131 case OMPScheduleType::NomergeUnorderedGuidedChunked:
132 case OMPScheduleType::NomergeUnorderedRuntime:
133 case OMPScheduleType::NomergeUnorderedAuto:
134 case OMPScheduleType::NomergeUnorderedTrapezoidal:
135 case OMPScheduleType::NomergeUnorderedGreedy:
136 case OMPScheduleType::NomergeUnorderedBalanced:
137 case OMPScheduleType::NomergeUnorderedGuidedIterativeChunked:
138 case OMPScheduleType::NomergeUnorderedGuidedAnalyticalChunked:
139 case OMPScheduleType::NomergeUnorderedSteal:
140 case OMPScheduleType::NomergeOrderedStaticChunked:
141 case OMPScheduleType::NomergeOrderedStatic:
142 case OMPScheduleType::NomergeOrderedDynamicChunked:
143 case OMPScheduleType::NomergeOrderedGuidedChunked:
144 case OMPScheduleType::NomergeOrderedRuntime:
145 case OMPScheduleType::NomergeOrderedAuto:
146 case OMPScheduleType::NomergeOrderedTrapezoidal:
147 case OMPScheduleType::OrderedDistributeChunked:
148 case OMPScheduleType::OrderedDistribute:
149 break;
150 default:
151 return false;
152 }
153
154 // Must not set both monotonicity modifiers at the same time.
155 OMPScheduleType MonotonicityFlags =
156 SchedType & OMPScheduleType::MonotonicityMask;
157 if (MonotonicityFlags == OMPScheduleType::MonotonicityMask)
158 return false;
159
160 return true;
161}
162#endif
163
164/// This is a wrapper over IRBuilderBase::restoreIP that also restores a current
165/// debug location when the insert point is at the end of a block. It picks a
166/// location scoped to the current function: the block's last instruction
167/// location if the block is non-empty, otherwise a location synthesized from
168/// the function's subprogram (when the function has debug info).
171 Builder.restoreIP(IP);
172 // When IP points at a real instruction, restoreIP (SetInsertPoint) already
173 // set the debug location from that instruction, so leave it alone.
174 llvm::BasicBlock *BB = Builder.GetInsertBlock();
175 if (Builder.GetInsertPoint() != BB->end())
176 return;
177
178 // At the end of a block, pick a location guaranteed to belong to the current
179 // insertion function's subprogram. Prefer the block's own last instruction;
180 // otherwise synthesize a location from the function's subprogram.
181 if (!BB->empty())
182 Builder.SetCurrentDebugLocation(BB->back().getStableDebugLoc());
183 else if (llvm::DISubprogram *FSP =
184 BB->getParent() ? BB->getParent()->getSubprogram() : nullptr) {
185 unsigned Line = FSP->getScopeLine() ? FSP->getScopeLine() : FSP->getLine();
186 Builder.SetCurrentDebugLocation(
187 llvm::DILocation::get(FSP->getContext(), Line, /*Column=*/0, FSP));
188 }
189}
190
191static bool hasGridValue(const Triple &T) {
192 return T.isAMDGPU() || T.isNVPTX() || T.isSPIRV();
193}
194
195static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {
196 if (T.isAMDGPU()) {
197 StringRef Features =
198 Kernel->getFnAttribute("target-features").getValueAsString();
199 if (Features.count("+wavefrontsize64"))
202 }
203 if (T.isNVPTX())
205 if (T.isSPIRV())
207 llvm_unreachable("No grid value available for this architecture!");
208}
209
210/// Determine which scheduling algorithm to use, determined from schedule clause
211/// arguments.
212static OMPScheduleType
213getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,
214 bool HasSimdModifier, bool HasDistScheduleChunks) {
215 // Currently, the default schedule it static.
216 switch (ClauseKind) {
217 case OMP_SCHEDULE_Default:
218 case OMP_SCHEDULE_Static:
219 return HasChunks ? OMPScheduleType::BaseStaticChunked
220 : OMPScheduleType::BaseStatic;
221 case OMP_SCHEDULE_Dynamic:
222 return OMPScheduleType::BaseDynamicChunked;
223 case OMP_SCHEDULE_Guided:
224 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd
225 : OMPScheduleType::BaseGuidedChunked;
226 case OMP_SCHEDULE_Auto:
228 case OMP_SCHEDULE_Runtime:
229 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd
230 : OMPScheduleType::BaseRuntime;
231 case OMP_SCHEDULE_Distribute:
232 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked
233 : OMPScheduleType::BaseDistribute;
234 }
235 llvm_unreachable("unhandled schedule clause argument");
236}
237
238/// Adds ordering modifier flags to schedule type.
239static OMPScheduleType
241 bool HasOrderedClause) {
242 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==
243 OMPScheduleType::None &&
244 "Must not have ordering nor monotonicity flags already set");
245
246 OMPScheduleType OrderingModifier = HasOrderedClause
247 ? OMPScheduleType::ModifierOrdered
248 : OMPScheduleType::ModifierUnordered;
249 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;
250
251 // Unsupported combinations
252 if (OrderingScheduleType ==
253 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))
254 return OMPScheduleType::OrderedGuidedChunked;
255 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |
256 OMPScheduleType::ModifierOrdered))
257 return OMPScheduleType::OrderedRuntime;
258
259 return OrderingScheduleType;
260}
261
262/// Adds monotonicity modifier flags to schedule type.
263static OMPScheduleType
265 bool HasSimdModifier, bool HasMonotonic,
266 bool HasNonmonotonic, bool HasOrderedClause) {
267 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==
268 OMPScheduleType::None &&
269 "Must not have monotonicity flags already set");
270 assert((!HasMonotonic || !HasNonmonotonic) &&
271 "Monotonic and Nonmonotonic are contradicting each other");
272
273 if (HasMonotonic) {
274 return ScheduleType | OMPScheduleType::ModifierMonotonic;
275 } else if (HasNonmonotonic) {
276 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
277 } else {
278 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.
279 // If the static schedule kind is specified or if the ordered clause is
280 // specified, and if the nonmonotonic modifier is not specified, the
281 // effect is as if the monotonic modifier is specified. Otherwise, unless
282 // the monotonic modifier is specified, the effect is as if the
283 // nonmonotonic modifier is specified.
284 OMPScheduleType BaseScheduleType =
285 ScheduleType & ~OMPScheduleType::ModifierMask;
286 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||
287 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||
288 HasOrderedClause) {
289 // The monotonic is used by default in openmp runtime library, so no need
290 // to set it.
291 return ScheduleType;
292 } else {
293 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
294 }
295 }
296}
297
298/// Determine the schedule type using schedule and ordering clause arguments.
299static OMPScheduleType
300computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,
301 bool HasSimdModifier, bool HasMonotonicModifier,
302 bool HasNonmonotonicModifier, bool HasOrderedClause,
303 bool HasDistScheduleChunks) {
305 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);
306 OMPScheduleType OrderedSchedule =
307 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);
309 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,
310 HasNonmonotonicModifier, HasOrderedClause);
311
313 return Result;
314}
315
316/// Given a function, if it represents the entry point of a target kernel, this
317/// returns the execution mode flags associated with that kernel.
318static std::optional<omp::OMPTgtExecModeFlags>
320 CallInst *TargetInitCall = nullptr;
321 for (Instruction &Inst : Kernel.getEntryBlock()) {
322 if (auto *Call = dyn_cast<CallInst>(&Inst)) {
323 if (Call->getCalledFunction()->getName() == "__kmpc_target_init") {
324 TargetInitCall = Call;
325 break;
326 }
327 }
328 }
329
330 if (!TargetInitCall)
331 return std::nullopt;
332
333 // Get the kernel mode information from the global variable associated to the
334 // first argument to the call to __kmpc_target_init. Refer to
335 // createTargetInit() to see how this is initialized.
336 Value *InitOperand = TargetInitCall->getArgOperand(0);
337 GlobalVariable *KernelEnv = nullptr;
338 if (auto *Cast = dyn_cast<ConstantExpr>(InitOperand))
339 KernelEnv = cast<GlobalVariable>(Cast->getOperand(0));
340 else
341 KernelEnv = cast<GlobalVariable>(InitOperand);
342 auto *KernelEnvInit = cast<ConstantStruct>(KernelEnv->getInitializer());
343 auto *ConfigEnv = cast<ConstantStruct>(KernelEnvInit->getOperand(0));
344 auto *KernelMode = cast<ConstantInt>(ConfigEnv->getOperand(2));
345 return static_cast<OMPTgtExecModeFlags>(KernelMode->getZExtValue());
346}
347
348static bool isGenericKernel(Function &Fn) {
349 std::optional<omp::OMPTgtExecModeFlags> ExecMode =
351 return !ExecMode || (*ExecMode & OMP_TGT_EXEC_MODE_GENERIC);
352}
353
354/// Make \p Source branch to \p Target.
355///
356/// Handles two situations:
357/// * \p Source already has an unconditional branch.
358/// * \p Source is a degenerate block (no terminator because the BB is
359/// the current head of the IR construction).
361 if (Instruction *Term = Source->getTerminatorOrNull()) {
362 auto *Br = cast<UncondBrInst>(Term);
363 BasicBlock *Succ = Br->getSuccessor();
364 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);
365 Br->setSuccessor(Target);
366 return;
367 }
368
369 auto *NewBr = UncondBrInst::Create(Target, Source);
370 NewBr->setDebugLoc(DL);
371}
372
374 bool CreateBranch, DebugLoc DL) {
375 assert(New->getFirstInsertionPt() == New->begin() &&
376 "Target BB must not have PHI nodes");
377
378 // Move instructions to new block.
379 BasicBlock *Old = IP.getBlock();
380 // If the `Old` block is empty then there are no instructions to move. But in
381 // the new debug scheme, it could have trailing debug records which will be
382 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2
383 // reasons:
384 // 1. If `New` is also empty, `BasicBlock::splice` crashes.
385 // 2. Even if `New` is not empty, the rationale to move those records to `New`
386 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function
387 // assumes that `Old` is optimized out and is going away. This is not the case
388 // here. The `Old` block is still being used e.g. a branch instruction is
389 // added to it later in this function.
390 // So we call `BasicBlock::splice` only when `Old` is not empty.
391 if (!Old->empty())
392 New->splice(New->begin(), Old, IP.getPoint(), Old->end());
393
394 if (CreateBranch) {
395 auto *NewBr = UncondBrInst::Create(New, Old);
396 NewBr->setDebugLoc(DL);
397 }
398}
399
400void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {
401 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
402 BasicBlock *Old = Builder.GetInsertBlock();
403
404 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);
405 if (CreateBranch)
406 Builder.SetInsertPoint(Old->getTerminator());
407 else
408 Builder.SetInsertPoint(Old);
409
410 // SetInsertPoint also updates the Builder's debug location, but we want to
411 // keep the one the Builder was configured to use.
412 Builder.SetCurrentDebugLocation(DebugLoc);
413}
414
416 DebugLoc DL, llvm::Twine Name) {
417 BasicBlock *Old = IP.getBlock();
419 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,
420 Old->getParent(), Old->getNextNode());
421 spliceBB(IP, New, CreateBranch, DL);
422 New->replaceSuccessorsPhiUsesWith(Old, New);
423 return New;
424}
425
426BasicBlock *llvm::splitBB(IRBuilderBase &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
440BasicBlock *llvm::splitBB(IRBuilder<> &Builder, bool CreateBranch,
441 llvm::Twine Name) {
442 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
443 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
444 if (CreateBranch)
445 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
446 else
447 Builder.SetInsertPoint(Builder.GetInsertBlock());
448 // SetInsertPoint also updates the Builder's debug location, but we want to
449 // keep the one the Builder was configured to use.
450 Builder.SetCurrentDebugLocation(DebugLoc);
451 return New;
452}
453
455 llvm::Twine Suffix) {
456 BasicBlock *Old = Builder.GetInsertBlock();
457 return splitBB(Builder, CreateBranch, Old->getName() + Suffix);
458}
459
460// This function creates a fake integer value and a fake use for the integer
461// value. It returns the fake value created. This is useful in modeling the
462// extra arguments to the outlined functions.
464 OpenMPIRBuilder::InsertPointTy OuterAllocaIP,
466 OpenMPIRBuilder::InsertPointTy InnerAllocaIP,
467 const Twine &Name = "", bool AsPtr = true,
468 bool Is64Bit = false) {
469 Builder.restoreIP(OuterAllocaIP);
470 IntegerType *IntTy = Is64Bit ? Builder.getInt64Ty() : Builder.getInt32Ty();
471 Instruction *FakeVal;
472 AllocaInst *FakeValAddr =
473 Builder.CreateAlloca(IntTy, nullptr, Name + ".addr");
474 ToBeDeleted.push_back(FakeValAddr);
475
476 if (AsPtr) {
477 FakeVal = FakeValAddr;
478 } else {
479 FakeVal = Builder.CreateLoad(IntTy, FakeValAddr, Name + ".val");
480 ToBeDeleted.push_back(FakeVal);
481 }
482
483 // Generate a fake use of this value
484 Builder.restoreIP(InnerAllocaIP);
485 Instruction *UseFakeVal;
486 if (AsPtr) {
487 UseFakeVal = Builder.CreateLoad(IntTy, FakeVal, Name + ".use");
488 } else {
489 UseFakeVal = cast<BinaryOperator>(Builder.CreateAdd(
490 FakeVal, Is64Bit ? Builder.getInt64(10) : Builder.getInt32(10)));
491 }
492 ToBeDeleted.push_back(UseFakeVal);
493 return FakeVal;
494}
495
496//===----------------------------------------------------------------------===//
497// OpenMPIRBuilderConfig
498//===----------------------------------------------------------------------===//
499
500namespace {
502/// Values for bit flags for marking which requires clauses have been used.
503enum OpenMPOffloadingRequiresDirFlags {
504 /// flag undefined.
505 OMP_REQ_UNDEFINED = 0x000,
506 /// no requires directive present.
507 OMP_REQ_NONE = 0x001,
508 /// reverse_offload clause.
509 OMP_REQ_REVERSE_OFFLOAD = 0x002,
510 /// unified_address clause.
511 OMP_REQ_UNIFIED_ADDRESS = 0x004,
512 /// unified_shared_memory clause.
513 OMP_REQ_UNIFIED_SHARED_MEMORY = 0x008,
514 /// dynamic_allocators clause.
515 OMP_REQ_DYNAMIC_ALLOCATORS = 0x010,
516 LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)
517};
518
519class OMPCodeExtractor : public CodeExtractor {
520public:
521 OMPCodeExtractor(OpenMPIRBuilder &OMPBuilder, ArrayRef<BasicBlock *> BBs,
522 DominatorTree *DT = nullptr, bool AggregateArgs = false,
523 BlockFrequencyInfo *BFI = nullptr,
524 BranchProbabilityInfo *BPI = nullptr,
525 AssumptionCache *AC = nullptr, bool AllowVarArgs = false,
526 bool AllowAlloca = false,
527 BasicBlock *AllocationBlock = nullptr,
528 ArrayRef<BasicBlock *> DeallocationBlocks = {},
529 std::string Suffix = "", bool ArgsInZeroAddressSpace = false)
530 : CodeExtractor(BBs, DT, AggregateArgs, BFI, BPI, AC, AllowVarArgs,
531 AllowAlloca, AllocationBlock, DeallocationBlocks, Suffix,
532 ArgsInZeroAddressSpace),
533 OMPBuilder(OMPBuilder) {}
534
535 virtual ~OMPCodeExtractor() = default;
536
537protected:
538 OpenMPIRBuilder &OMPBuilder;
539};
540
541class DeviceSharedMemCodeExtractor : public OMPCodeExtractor {
542public:
543 using OMPCodeExtractor::OMPCodeExtractor;
544 virtual ~DeviceSharedMemCodeExtractor() = default;
545
546protected:
547 virtual Instruction *
548 allocateVar(IRBuilder<>::InsertPoint AllocaIP, DebugLoc DL, Type *VarType,
549 const Twine &Name = Twine(""),
550 AddrSpaceCastInst **CastedAlloc = nullptr) override {
551 return OMPBuilder.createOMPAllocShared({AllocaIP, DL}, VarType, Name);
552 }
553
554 virtual Instruction *deallocateVar(IRBuilder<>::InsertPoint DeallocIP,
555 DebugLoc DL, Value *Var,
556 Type *VarType) override {
557 return OMPBuilder.createOMPFreeShared({DeallocIP, DL}, Var, VarType);
558 }
559};
560
561/// Helper storing information about regions to outline using device shared
562/// memory for intermediate allocations.
563struct DeviceSharedMemOutlineInfo : public OpenMPIRBuilder::OutlineInfo {
564 OpenMPIRBuilder &OMPBuilder;
565
566 DeviceSharedMemOutlineInfo(OpenMPIRBuilder &OMPBuilder)
567 : OMPBuilder(OMPBuilder) {}
568 virtual ~DeviceSharedMemOutlineInfo() = default;
569
570 virtual std::unique_ptr<CodeExtractor>
571 createCodeExtractor(ArrayRef<BasicBlock *> Blocks,
572 bool ArgsInZeroAddressSpace,
573 Twine Suffix = Twine("")) override;
574};
575
576} // anonymous namespace
577
579 : RequiresFlags(OMP_REQ_UNDEFINED) {}
580
583 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,
584 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)
587 RequiresFlags(OMP_REQ_UNDEFINED) {
588 if (HasRequiresReverseOffload)
589 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
590 if (HasRequiresUnifiedAddress)
591 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
592 if (HasRequiresUnifiedSharedMemory)
593 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
594 if (HasRequiresDynamicAllocators)
595 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
596}
597
599 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;
600}
601
603 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;
604}
605
607 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;
608}
609
611 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;
612}
613
615 return hasRequiresFlags() ? RequiresFlags
616 : static_cast<int64_t>(OMP_REQ_NONE);
617}
618
620 if (Value)
621 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
622 else
623 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;
624}
625
627 if (Value)
628 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
629 else
630 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;
631}
632
634 if (Value)
635 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
636 else
637 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;
638}
639
641 if (Value)
642 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
643 else
644 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;
645}
646
647//===----------------------------------------------------------------------===//
648// OpenMPIRBuilder
649//===----------------------------------------------------------------------===//
650
653 SmallVector<Value *> &ArgsVector) {
655 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);
656 auto Int32Ty = Type::getInt32Ty(Builder.getContext());
657 constexpr size_t MaxDim = 3;
658 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));
659
660 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);
661
662 Value *DynCGroupMemFallbackFlag =
663 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));
664 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);
665
666 Value *StrictBlocksFlag = Builder.getInt64(KernelArgs.StrictBlocks);
667 Value *StrictThreadsFlag = Builder.getInt64(KernelArgs.StrictThreads);
668
669 StrictBlocksFlag = Builder.CreateShl(StrictBlocksFlag, 6);
670 StrictThreadsFlag = Builder.CreateShl(StrictThreadsFlag, 7);
671
672 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
673 Flags = Builder.CreateOr(Flags, StrictBlocksFlag);
674 Flags = Builder.CreateOr(Flags, StrictThreadsFlag);
675
676 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
677
678 Value *NumTeams3D =
679 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
680 Value *NumThreads3D =
681 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
682 for (unsigned I :
683 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
684 NumTeams3D =
685 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
686 for (unsigned I :
687 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
688 NumThreads3D =
689 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
690
691 ArgsVector = {Version,
692 PointerNum,
693 KernelArgs.RTArgs.BasePointersArray,
694 KernelArgs.RTArgs.PointersArray,
695 KernelArgs.RTArgs.SizesArray,
696 KernelArgs.RTArgs.MapTypesArray,
697 KernelArgs.RTArgs.MapNamesArray,
698 KernelArgs.RTArgs.MappersArray,
699 KernelArgs.NumIterations,
700 Flags,
701 NumTeams3D,
702 NumThreads3D,
703 KernelArgs.DynCGroupMem};
704}
705
707 LLVMContext &Ctx = Fn.getContext();
708
709 // Get the function's current attributes.
710 auto Attrs = Fn.getAttributes();
711 auto FnAttrs = Attrs.getFnAttrs();
712 auto RetAttrs = Attrs.getRetAttrs();
714 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
715 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
716
717 // Add AS to FnAS while taking special care with integer extensions.
718 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
719 bool Param = true) -> void {
720 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
721 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
722 if (HasSignExt || HasZeroExt) {
723 assert(AS.getNumAttributes() == 1 &&
724 "Currently not handling extension attr combined with others.");
725 if (Param) {
726 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
727 FnAS = FnAS.addAttribute(Ctx, AK);
728 } else if (auto AK =
729 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
730 FnAS = FnAS.addAttribute(Ctx, AK);
731 } else {
732 FnAS = FnAS.addAttributes(Ctx, AS);
733 }
734 };
735
736#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
737#include "llvm/Frontend/OpenMP/OMPKinds.def"
738
739 // Add attributes to the function declaration.
740 switch (FnID) {
741#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
742 case Enum: \
743 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
744 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
745 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
746 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
747 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
748 break;
749#include "llvm/Frontend/OpenMP/OMPKinds.def"
750 default:
751 // Attributes are optional.
752 break;
753 }
754}
755
758 FunctionType *FnTy = nullptr;
759 Function *Fn = nullptr;
760
761 // Try to find the declation in the module first.
762 switch (FnID) {
763#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
764 case Enum: \
765 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
766 IsVarArg); \
767 Fn = M.getFunction(Str); \
768 break;
769#include "llvm/Frontend/OpenMP/OMPKinds.def"
770 }
771
772 if (!Fn) {
773 // Create a new declaration if we need one.
774 switch (FnID) {
775#define OMP_RTL(Enum, Str, ...) \
776 case Enum: \
777 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
778 break;
779#include "llvm/Frontend/OpenMP/OMPKinds.def"
780 }
781 Fn->setCallingConv(Config.getRuntimeCC());
782 // Add information if the runtime function takes a callback function
783 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
784 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
785 LLVMContext &Ctx = Fn->getContext();
786 MDBuilder MDB(Ctx);
787 // Annotate the callback behavior of the runtime function:
788 // - The callback callee is argument number 2 (microtask).
789 // - The first two arguments of the callback callee are unknown (-1).
790 // - All variadic arguments to the runtime function are passed to the
791 // callback callee.
792 Fn->addMetadata(
793 LLVMContext::MD_callback,
795 2, {-1, -1}, /* VarArgsArePassed */ true)}));
796 }
797 }
798
799 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
800 << " with type " << *Fn->getFunctionType() << "\n");
801 addAttributes(FnID, *Fn);
802
803 } else {
804 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
805 << " with type " << *Fn->getFunctionType() << "\n");
806 }
807
808 assert(Fn && "Failed to create OpenMP runtime function");
809
810 return {FnTy, Fn};
811}
812
815 if (!FiniBB) {
816 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
818 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
819 Builder.SetInsertPoint(FiniBB);
820 // FiniCB adds the branch to the exit stub.
821 if (Error Err = FiniCB(Builder.saveIP()))
822 return Err;
823 }
824 return FiniBB;
825}
826
828 BasicBlock *OtherFiniBB) {
829 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
830 if (!FiniBB) {
831 FiniBB = OtherFiniBB;
832
833 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
834 if (Error Err = FiniCB(Builder.saveIP()))
835 return Err;
836
837 return Error::success();
838 }
839
840 // Move instructions from FiniBB to the start of OtherFiniBB.
841 auto EndIt = FiniBB->end();
842 if (FiniBB->size() >= 1)
843 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
844 EndIt = Prev;
845 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
846 EndIt);
847
848 FiniBB->replaceAllUsesWith(OtherFiniBB);
849 FiniBB->eraseFromParent();
850 FiniBB = OtherFiniBB;
851 return Error::success();
852}
853
856 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
857 assert(Fn && "Failed to create OpenMP runtime function pointer");
858 return Fn;
859}
860
863 StringRef Name) {
864 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
865 Call->setCallingConv(Config.getRuntimeCC());
866 return Call;
867}
868
869void OpenMPIRBuilder::initialize() { initializeTypes(M); }
870
873 BasicBlock &EntryBlock = Function->getEntryBlock();
874 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
875
876 // Loop over blocks looking for constant allocas, skipping the entry block
877 // as any allocas there are already in the desired location.
878 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
879 Block++) {
880 for (auto Inst = Block->getReverseIterator()->begin();
881 Inst != Block->getReverseIterator()->end();) {
883 Inst++;
885 continue;
886 AllocaInst->moveBeforePreserving(MoveLocInst);
887 } else {
888 Inst++;
889 }
890 }
891 }
892}
893
896
897 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
898 // TODO: For now, we support simple static allocations, we might need to
899 // move non-static ones as well. However, this will need further analysis to
900 // move the lenght arguments as well.
902 };
903
904 for (llvm::Instruction &Inst : Block)
906 if (ShouldHoistAlloca(*AllocaInst))
907 AllocasToMove.push_back(AllocaInst);
908
909 auto InsertPoint =
910 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
911
912 for (llvm::Instruction *AllocaInst : AllocasToMove)
914}
915
917 PostDominatorTree PostDomTree(*Func);
918 for (llvm::BasicBlock &BB : *Func)
919 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
921}
922
924 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
926 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
927 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
928 // Skip functions that have not finalized yet; may happen with nested
929 // function generation.
930 if (Fn && OI->getFunction() != Fn) {
931 DeferredOutlines.push_back(std::move(OI));
932 continue;
933 }
934
935 ParallelRegionBlockSet.clear();
936 Blocks.clear();
937 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
938
939 Function *OuterFn = OI->getFunction();
940 CodeExtractorAnalysisCache CEAC(*OuterFn);
941 // If we generate code for the target device, we need to allocate
942 // struct for aggregate params in the device default alloca address space.
943 // OpenMP runtime requires that the params of the extracted functions are
944 // passed as zero address space pointers. This flag ensures that
945 // CodeExtractor generates correct code for extracted functions
946 // which are used by OpenMP runtime.
947 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
948 std::unique_ptr<CodeExtractor> Extractor =
949 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
950
951 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
952 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
953 << " Exit: " << OI->ExitBB->getName() << "\n");
954 assert(Extractor->isEligible() &&
955 "Expected OpenMP outlining to be possible!");
956
957 for (auto *V : OI->ExcludeArgsFromAggregate)
958 Extractor->excludeArgFromAggregate(V);
959
960 Function *OutlinedFn =
961 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
962
963 // Forward target-cpu, target-features attributes to the outlined function.
964 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
965 if (TargetCpuAttr.isStringAttribute())
966 OutlinedFn->addFnAttr(TargetCpuAttr);
967
968 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
969 if (TargetFeaturesAttr.isStringAttribute())
970 OutlinedFn->addFnAttr(TargetFeaturesAttr);
971
972 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
973 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
974 assert(OutlinedFn->getReturnType()->isVoidTy() &&
975 "OpenMP outlined functions should not return a value!");
976
977 // For compability with the clang CG we move the outlined function after the
978 // one with the parallel region.
979 OutlinedFn->removeFromParent();
980 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
981
982 // Remove the artificial entry introduced by the extractor right away, we
983 // made our own entry block after all.
984 {
985 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
986 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
987 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
988 // Move instructions from the to-be-deleted ArtificialEntry to the entry
989 // basic block of the parallel region. CodeExtractor generates
990 // instructions to unwrap the aggregate argument and may sink
991 // allocas/bitcasts for values that are solely used in the outlined region
992 // and do not escape.
993 assert(!ArtificialEntry.empty() &&
994 "Expected instructions to add in the outlined region entry");
995 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
996 End = ArtificialEntry.rend();
997 It != End;) {
998 Instruction &I = *It;
999 It++;
1000
1001 if (I.isTerminator()) {
1002 // Absorb any debug value that terminator may have
1003 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
1004 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
1005 continue;
1006 }
1007
1008 I.moveBeforePreserving(*OI->EntryBB,
1009 OI->EntryBB->getFirstInsertionPt());
1010 }
1011
1012 OI->EntryBB->moveBefore(&ArtificialEntry);
1013 ArtificialEntry.eraseFromParent();
1014 }
1015 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
1016 assert(OutlinedFn && OutlinedFn->hasNUses(1));
1017
1018 // Run a user callback, e.g. to add attributes.
1019 if (OI->PostOutlineCB)
1020 OI->PostOutlineCB(*OutlinedFn);
1021
1022 if (OI->FixUpNonEntryAllocas)
1024 }
1025
1026 // Remove work items that have been completed.
1027 OutlineInfos = std::move(DeferredOutlines);
1028
1029 // The createTarget functions embeds user written code into
1030 // the target region which may inject allocas which need to
1031 // be moved to the entry block of our target or risk malformed
1032 // optimisations by later passes, this is only relevant for
1033 // the device pass which appears to be a little more delicate
1034 // when it comes to optimisations (however, we do not block on
1035 // that here, it's up to the inserter to the list to do so).
1036 // This notbaly has to occur after the OutlinedInfo candidates
1037 // have been extracted so we have an end product that will not
1038 // be implicitly adversely affected by any raises unless
1039 // intentionally appended to the list.
1040 // NOTE: This only does so for ConstantData, it could be extended
1041 // to ConstantExpr's with further effort, however, they should
1042 // largely be folded when they get here. Extending it to runtime
1043 // defined/read+writeable allocation sizes would be non-trivial
1044 // (need to factor in movement of any stores to variables the
1045 // allocation size depends on, as well as the usual loads,
1046 // otherwise it'll yield the wrong result after movement) and
1047 // likely be more suitable as an LLVM optimisation pass.
1050
1051 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1052 [](EmitMetadataErrorKind Kind,
1053 const TargetRegionEntryInfo &EntryInfo) -> void {
1054 errs() << "Error of kind: " << Kind
1055 << " when emitting offload entries and metadata during "
1056 "OMPIRBuilder finalization \n";
1057 };
1058
1059 if (!OffloadInfoManager.empty())
1061
1062 // Rewrite uses of globals to their replacement declare target globals if
1063 // we are processing a device module.
1064 if (Config.isTargetDevice())
1065 applyDeclareTargetGlobalReplacements();
1066
1067 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1068 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1069 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1070 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1071 }
1072
1073 IsFinalized = true;
1074}
1075
1076bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1077
1079 GlobalValue *Original, GlobalValue *Replacement) {
1080 assert(Original && Replacement &&
1081 "Null values provided to registerDeclareTargetGlobalReplacement");
1082 DeclareTargetGlobalReplacements.push_back({Original, Replacement});
1083}
1084
1085void OpenMPIRBuilder::applyDeclareTargetGlobalReplacements() {
1086 for (DeclareTargetGlobalReplacement &R : DeclareTargetGlobalReplacements) {
1087 GlobalValue *OldGV = R.Original;
1088 GlobalValue *NewGV = R.Replacement;
1089
1090 assert(OldGV && NewGV &&
1091 "A null value was inserted into DeclareTargetGlobalReplacements");
1092
1093 // The assert above should catch this case, but this is kept to attempt
1094 // to proceed without issue when asserts are off.
1095 if (!OldGV || !NewGV)
1096 continue;
1097
1098 // The replacement global is a reference pointer that holds the
1099 // address of the device-resident storage. Every use must load the
1100 // reference pointer first and use the loaded address.
1101 //
1102 // Constant expression users (e.g. a constant GEP embedded in another
1103 // global's initializer or in an instruction) cannot have a load inserted
1104 // in place, so first expand any constant-expression users that live inside
1105 // functions into instructions. Any remaining constant users are handled
1106 // via a direct constant rewrite below as we cannot materialize a load
1107 // there.
1108 //
1109 // NOTE: We extend the constant rewrite to module scope, as we replace all
1110 // usages.
1111 if (auto *OldConst = dyn_cast<Constant>(OldGV))
1113 /*RestrictToFunc=*/nullptr,
1114 /*RemoveDeadConstants=*/false);
1115
1116 IRBuilderBase::InsertPointGuard Guard(Builder);
1118 for (User *U : Users) {
1119 auto *Insn = dyn_cast<Instruction>(U);
1120 if (!Insn)
1121 continue;
1122
1123 // A PHI node cannot have a load inserted immediately before it, as PHIs
1124 // must remain grouped at the top of their basic block. So we need to
1125 // make sure any loads we emit are generated in the preceding edge, a
1126 // PHI may reference the global on more than one edge, so every matching
1127 // slot must be handled.
1128 if (auto *PHI = dyn_cast<PHINode>(Insn)) {
1129 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I < E; ++I) {
1130 if (PHI->getIncomingValue(I) != OldGV)
1131 continue;
1132
1133 BasicBlock *IncomingBB = PHI->getIncomingBlock(I);
1134 Builder.SetInsertPoint(IncomingBB->getTerminator());
1135 Builder.SetCurrentDebugLocation(PHI->getDebugLoc());
1136 LoadInst *EdgeLoad = Builder.CreateLoad(NewGV->getType(), NewGV);
1137 PHI->setIncomingValue(I, EdgeLoad);
1138 }
1139 continue;
1140 }
1141
1142 Builder.SetInsertPoint(Insn);
1143 Builder.SetCurrentDebugLocation(Insn->getDebugLoc());
1144 LoadInst *Load = Builder.CreateLoad(NewGV->getType(), NewGV);
1145
1146 // The replacement declare target global lives in the default address
1147 // space, whereas the original global may reside in a non-default
1148 // address space. In that case the initial lowering may have
1149 // emitted an addrspacecast that is no longer valid. Replace the
1150 // whole addrspacecast with the load and erase it rather than
1151 // feeding the load back into the (now pointless) cast.
1152 // NOTE: If we end up with replacement declare target globals in
1153 // non-zero AS's the below will need some minor extensions to have the
1154 // option to alter the address space cast to the new address space where
1155 // required rather than just replacing it.
1156 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Insn)) {
1157 unsigned NewGVAS = NewGV->getType()->getPointerAddressSpace();
1158 assert(NewGVAS == 0 &&
1159 "Non-default address space declare target global");
1160 unsigned OldGVAS = OldGV->getType()->getPointerAddressSpace();
1161 unsigned DestAS = ASC->getType()->getPointerAddressSpace();
1162 if (DestAS == 0 && NewGVAS != OldGVAS) {
1163 ASC->replaceAllUsesWith(Load);
1164 ASC->eraseFromParent();
1165 continue;
1166 }
1167 }
1168
1169 Insn->replaceUsesOfWith(OldGV, Load);
1170 }
1171 }
1172
1174}
1175
1177 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1178}
1179
1181 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1182 auto *GV =
1183 new GlobalVariable(M, I32Ty,
1184 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1185 ConstantInt::get(I32Ty, Value), Name);
1186 GV->setVisibility(GlobalValue::HiddenVisibility);
1187
1188 return GV;
1189}
1190
1192 if (List.empty())
1193 return;
1194
1195 // Convert List to what ConstantArray needs.
1197 UsedArray.resize(List.size());
1198 for (unsigned I = 0, E = List.size(); I != E; ++I)
1200 cast<Constant>(&*List[I]), Builder.getPtrTy());
1201
1202 if (UsedArray.empty())
1203 return;
1204 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1205
1206 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1207 ConstantArray::get(ATy, UsedArray), Name);
1208
1209 GV->setSection("llvm.metadata");
1210}
1211
1214 OMPTgtExecModeFlags Mode) {
1215 auto *Int8Ty = Builder.getInt8Ty();
1216 auto *GVMode = new GlobalVariable(
1217 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1218 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1219 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1220 return GVMode;
1221}
1222
1224 uint32_t SrcLocStrSize,
1225 IdentFlag LocFlags,
1226 unsigned Reserve2Flags) {
1227 // Enable "C-mode".
1228 LocFlags |= OMP_IDENT_FLAG_KMPC;
1229
1230 Constant *&Ident =
1231 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1232 if (!Ident) {
1233 Constant *I32Null = ConstantInt::getNullValue(Int32);
1234 Constant *IdentData[] = {I32Null,
1235 ConstantInt::get(Int32, uint32_t(LocFlags)),
1236 ConstantInt::get(Int32, Reserve2Flags),
1237 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1238
1239 size_t SrcLocStrArgIdx = 4;
1240 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1242 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1243 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1244 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1245 Constant *Initializer =
1246 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1247
1248 // Look for existing encoding of the location + flags, not needed but
1249 // minimizes the difference to the existing solution while we transition.
1250 for (GlobalVariable &GV : M.globals())
1251 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1252 if (GV.getInitializer() == Initializer)
1253 Ident = &GV;
1254
1255 if (!Ident) {
1256 auto *GV = new GlobalVariable(
1257 M, OpenMPIRBuilder::Ident,
1258 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1260 M.getDataLayout().getDefaultGlobalsAddressSpace());
1261 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1262 GV->setAlignment(Align(8));
1263 Ident = GV;
1264 }
1265 }
1266
1267 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1268}
1269
1271 uint32_t &SrcLocStrSize) {
1272 SrcLocStrSize = LocStr.size();
1273 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1274 if (!SrcLocStr) {
1275 Constant *Initializer =
1276 ConstantDataArray::getString(M.getContext(), LocStr);
1277
1278 // Look for existing encoding of the location, not needed but minimizes the
1279 // difference to the existing solution while we transition.
1280 for (GlobalVariable &GV : M.globals())
1281 if (GV.isConstant() && GV.hasInitializer() &&
1282 GV.getInitializer() == Initializer)
1283 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1284
1285 SrcLocStr = Builder.CreateGlobalString(
1286 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1287 &M);
1288 }
1289 return SrcLocStr;
1290}
1291
1293 StringRef FileName,
1294 unsigned Line, unsigned Column,
1295 uint32_t &SrcLocStrSize) {
1296 SmallString<128> Buffer;
1297 Buffer.push_back(';');
1298 Buffer.append(FileName);
1299 Buffer.push_back(';');
1300 Buffer.append(FunctionName);
1301 Buffer.push_back(';');
1302 Buffer.append(std::to_string(Line));
1303 Buffer.push_back(';');
1304 Buffer.append(std::to_string(Column));
1305 Buffer.push_back(';');
1306 Buffer.push_back(';');
1307 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1308}
1309
1310Constant *
1312 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1313 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1314}
1315
1317 uint32_t &SrcLocStrSize,
1318 Function *F) {
1319 DILocation *DIL = DL.get();
1320 if (!DIL)
1321 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1322 StringRef FileName =
1323 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1324 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1325 if (Function.empty() && F)
1326 Function = F->getName();
1327 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1328 DIL->getColumn(), SrcLocStrSize);
1329}
1330
1332 uint32_t &SrcLocStrSize) {
1333 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1334 Loc.IP.getBlock()->getParent());
1335}
1336
1339 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1340 "omp_global_thread_num");
1341}
1342
1343OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1344 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1345 ArrayRef<Type *> ResultPtrTys,
1346 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1347 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1348 "expected one result pointer type per in_reduction item");
1349 if (!updateToLocation(Loc))
1350 return Loc.IP;
1351 if (OrigPtrs.empty())
1352 return Builder.saveIP();
1353
1354 // Compute the executing thread's gtid once for the whole target body and
1355 // reuse it for every in_reduction lookup, so a target with several
1356 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1357 // item.
1358 uint32_t SrcLocStrSize;
1359 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1360 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1361 Value *Gtid = getOrCreateThreadID(Ident);
1362
1363 // The runtime entry point takes (and returns) a generic, default-address-
1364 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1365 // taskgroups to find the matching task_reduction registration for the item.
1366 Type *PtrTy = PointerType::getUnqual(M.getContext());
1367 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1368 FunctionCallee GetThData =
1369 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1370
1371 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1372 // Normalize a non-default-address-space original pointer to the generic
1373 // address space before the call.
1374 Value *OrigPtr = OrigPtrs[Idx];
1375 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1376 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1377 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1378
1379 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1380 "omp.inred.priv");
1381
1382 // Cast the returned private pointer back to the requested address space
1383 // when it differs.
1384 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1385 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1386 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1387
1388 MapPrivateCB(Idx, Priv);
1389 }
1390 return Builder.saveIP();
1391}
1392
1395 bool ForceSimpleCall, bool CheckCancelFlag) {
1396 if (!updateToLocation(Loc))
1397 return Loc.IP;
1398
1399 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1400 // __kmpc_barrier(loc, thread_id);
1401
1402 IdentFlag BarrierLocFlags;
1403 switch (Kind) {
1404 case OMPD_for:
1405 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1406 break;
1407 case OMPD_sections:
1408 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1409 break;
1410 case OMPD_single:
1411 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1412 break;
1413 case OMPD_barrier:
1414 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1415 break;
1416 default:
1417 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1418 break;
1419 }
1420
1421 uint32_t SrcLocStrSize;
1422 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1423 Value *Args[] = {
1424 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1425 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1426
1427 // If we are in a cancellable parallel region, barriers are cancellation
1428 // points.
1429 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1430 bool UseCancelBarrier =
1431 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1432
1434 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1435 ? OMPRTL___kmpc_cancel_barrier
1436 : OMPRTL___kmpc_barrier),
1437 Args);
1438
1439 if (UseCancelBarrier && CheckCancelFlag)
1440 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1441 return Err;
1442
1443 return Builder.saveIP();
1444}
1445
1448 Value *IfCondition,
1449 omp::Directive CanceledDirective) {
1450 if (!updateToLocation(Loc))
1451 return Loc.IP;
1452
1453 // LLVM utilities like blocks with terminators.
1454 auto *UI = Builder.CreateUnreachable();
1455
1456 Instruction *ThenTI = UI, *ElseTI = nullptr;
1457 if (IfCondition) {
1458 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1459
1460 // Even if the if condition evaluates to false, this should count as a
1461 // cancellation point
1462 Builder.SetInsertPoint(ElseTI);
1463 auto ElseIP = Builder.saveIP();
1464
1466 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1467 if (!IPOrErr)
1468 return IPOrErr;
1469 }
1470
1471 Builder.SetInsertPoint(ThenTI);
1472
1473 Value *CancelKind = nullptr;
1474 switch (CanceledDirective) {
1475#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1476 case DirectiveEnum: \
1477 CancelKind = Builder.getInt32(Value); \
1478 break;
1479#include "llvm/Frontend/OpenMP/OMPKinds.def"
1480 default:
1481 llvm_unreachable("Unknown cancel kind!");
1482 }
1483
1484 uint32_t SrcLocStrSize;
1485 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1486 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1487 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1489 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1490
1491 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1492 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1493 return Err;
1494
1495 // Update the insertion point and remove the terminator we introduced.
1496 Builder.SetInsertPoint(UI->getParent());
1497 UI->eraseFromParent();
1498
1499 return Builder.saveIP();
1500}
1501
1504 omp::Directive CanceledDirective) {
1505 if (!updateToLocation(Loc))
1506 return Loc.IP;
1507
1508 // LLVM utilities like blocks with terminators.
1509 auto *UI = Builder.CreateUnreachable();
1510 Builder.SetInsertPoint(UI);
1511
1512 Value *CancelKind = nullptr;
1513 switch (CanceledDirective) {
1514#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1515 case DirectiveEnum: \
1516 CancelKind = Builder.getInt32(Value); \
1517 break;
1518#include "llvm/Frontend/OpenMP/OMPKinds.def"
1519 default:
1520 llvm_unreachable("Unknown cancel kind!");
1521 }
1522
1523 uint32_t SrcLocStrSize;
1524 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1525 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1526 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1528 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1529
1530 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1531 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1532 return Err;
1533
1534 // Update the insertion point and remove the terminator we introduced.
1535 Builder.SetInsertPoint(UI->getParent());
1536 UI->eraseFromParent();
1537
1538 return Builder.saveIP();
1539}
1540
1542 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1543 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1544 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1545 if (!updateToLocation(Loc))
1546 return Loc.IP;
1547
1548 Builder.restoreIP(AllocaIP);
1549 auto *KernelArgsPtr =
1550 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1552
1553 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1554 llvm::Value *Arg =
1555 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1556 Builder.CreateAlignedStore(
1557 KernelArgs[I], Arg,
1558 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1559 }
1560
1561 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1562 NumThreads, HostPtr, KernelArgsPtr};
1563
1565 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1566 OffloadingArgs);
1567
1568 return Builder.saveIP();
1569}
1570
1572 const LocationDescription &Loc, Value *OutlinedFnID,
1573 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1574 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1575
1576 if (!updateToLocation(Loc))
1577 return Loc.IP;
1578
1579 // On top of the arrays that were filled up, the target offloading call
1580 // takes as arguments the device id as well as the host pointer. The host
1581 // pointer is used by the runtime library to identify the current target
1582 // region, so it only has to be unique and not necessarily point to
1583 // anything. It could be the pointer to the outlined function that
1584 // implements the target region, but we aren't using that so that the
1585 // compiler doesn't need to keep that, and could therefore inline the host
1586 // function if proven worthwhile during optimization.
1587
1588 // From this point on, we need to have an ID of the target region defined.
1589 assert(OutlinedFnID && "Invalid outlined function ID!");
1590 (void)OutlinedFnID;
1591
1592 // Return value of the runtime offloading call.
1593 Value *Return = nullptr;
1594
1595 // Arguments for the target kernel.
1596 SmallVector<Value *> ArgsVector;
1597 getKernelArgsVector(Args, Builder, ArgsVector);
1598
1599 // The target region is an outlined function launched by the runtime
1600 // via calls to __tgt_target_kernel().
1601 //
1602 // Note that on the host and CPU targets, the runtime implementation of
1603 // these calls simply call the outlined function without forking threads.
1604 // The outlined functions themselves have runtime calls to
1605 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1606 // the compiler in emitTeamsCall() and emitParallelCall().
1607 //
1608 // In contrast, on the NVPTX target, the implementation of
1609 // __tgt_target_teams() launches a GPU kernel with the requested number
1610 // of teams and threads so no additional calls to the runtime are required.
1611 // Check the error code and execute the host version if required.
1612 Builder.restoreIP(emitTargetKernel(
1613 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1614 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1615
1616 BasicBlock *OffloadFailedBlock =
1617 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1618 BasicBlock *OffloadContBlock =
1619 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1620 Value *Failed = Builder.CreateIsNotNull(Return);
1621 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1622
1623 auto CurFn = Builder.GetInsertBlock()->getParent();
1624 emitBlock(OffloadFailedBlock, CurFn);
1625 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1626 if (!AfterIP)
1627 return AfterIP.takeError();
1628 Builder.restoreIP(*AfterIP);
1629 emitBranch(OffloadContBlock);
1630 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1631 return Builder.saveIP();
1632}
1633
1635 Value *CancelFlag, omp::Directive CanceledDirective) {
1636 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1637 "Unexpected cancellation!");
1638
1639 // For a cancel barrier we create two new blocks.
1640 BasicBlock *BB = Builder.GetInsertBlock();
1641 BasicBlock *NonCancellationBlock;
1642 if (Builder.GetInsertPoint() == BB->end()) {
1643 // TODO: This branch will not be needed once we moved to the
1644 // OpenMPIRBuilder codegen completely.
1645 NonCancellationBlock = BasicBlock::Create(
1646 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1647 } else {
1648 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1650 Builder.SetInsertPoint(BB);
1651 }
1652 BasicBlock *CancellationBlock = BasicBlock::Create(
1653 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1654
1655 // Jump to them based on the return value.
1656 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1657 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1658 /* TODO weight */ nullptr, nullptr);
1659
1660 // From the cancellation block we finalize all variables and go to the
1661 // post finalization block that is known to the FiniCB callback.
1662 auto &FI = FinalizationStack.back();
1663 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1664 if (!FiniBBOrErr)
1665 return FiniBBOrErr.takeError();
1666 Builder.SetInsertPoint(CancellationBlock);
1667 Builder.CreateBr(*FiniBBOrErr);
1668
1669 // The continuation block is where code generation continues.
1670 Builder.SetInsertPoint(NonCancellationBlock, NonCancellationBlock->begin());
1671 return Error::success();
1672}
1673
1674/// Create wrapper function used to gather the outlined function's argument
1675/// structure from a shared buffer and to forward them to it when running in
1676/// Generic mode.
1677///
1678/// The outlined function is expected to receive 2 integer arguments followed by
1679/// an optional pointer argument to an argument structure holding the rest.
1681 Function &OutlinedFn) {
1682 size_t NumArgs = OutlinedFn.arg_size();
1683 assert((NumArgs == 2 || NumArgs == 3) &&
1684 "expected a 2-3 argument parallel outlined function");
1685 bool UseArgStruct = NumArgs == 3;
1686
1687 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1688 IRBuilder<>::InsertPointGuard IPG(Builder);
1689 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1690 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1691 /*isVarArg=*/false);
1692 auto *WrapperFn =
1694 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1695
1696 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1697 WrapperFn->addParamAttr(0, Attribute::ZExt);
1698 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1699
1700 BasicBlock *EntryBB =
1701 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1702 Builder.SetInsertPoint(EntryBB);
1703
1704 // Allocation.
1705 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1706 /*ArraySize=*/nullptr, "addr");
1707 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1708 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1709 AddrAlloca->getName() + ".ascast");
1710
1711 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1712 /*ArraySize=*/nullptr, "zero");
1713 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1714 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1715 ZeroAlloca->getName() + ".ascast");
1716
1717 Value *ArgsAlloca = nullptr;
1718 if (UseArgStruct) {
1719 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1720 /*ArraySize=*/nullptr, "global_args");
1721 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1722 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1723 ArgsAlloca->getName() + ".ascast");
1724 }
1725
1726 // Initialization.
1727 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1728 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1729 if (UseArgStruct) {
1730 Builder.CreateCall(
1731 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1732 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1733 {ArgsAlloca});
1734 }
1735
1736 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1737
1738 // Load structArg from global_args.
1739 if (UseArgStruct) {
1740 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1741 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1742 {Builder.getInt64(0)});
1743 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1744 Args.push_back(StructArg);
1745 }
1746
1747 // Call the outlined function holding the parallel body.
1748 Builder.CreateCall(&OutlinedFn, Args);
1749 Builder.CreateRetVoid();
1750
1751 return WrapperFn;
1752}
1753
1754// Callback used to create OpenMP runtime calls to support
1755// omp parallel clause for the device.
1756// We need to use this callback to replace call to the OutlinedFn in OuterFn
1757// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1759 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1760 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1761 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1762 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1763 assert(OutlinedFn.arg_size() >= 2 &&
1764 "Expected at least tid and bounded tid as arguments");
1765 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1766
1767 // Add some known attributes.
1768 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1769 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1770 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1771 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1772 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1773 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1774
1775 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1776 assert(CI && "Expected call instruction to outlined function");
1777 CI->getParent()->setName("omp_parallel");
1778
1779 Builder.SetInsertPoint(CI);
1780 Type *PtrTy = OMPIRBuilder->VoidPtr;
1781
1782 // Add alloca for kernel args
1783 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1784 Builder.SetInsertPoint(OuterAllocaBB, OuterAllocaBB->getFirstInsertionPt());
1785 AllocaInst *ArgsAlloca =
1786 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1787 Value *Args = ArgsAlloca;
1788 // Add address space cast if array for storing arguments is not allocated
1789 // in address space 0
1790 if (ArgsAlloca->getAddressSpace())
1791 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1792 Builder.restoreIP(CurrentIP);
1793
1794 // Store captured vars which are used by kmpc_parallel_60
1795 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1796 Value *V = *(CI->arg_begin() + 2 + Idx);
1797 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1798 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1799 Builder.CreateStore(V, StoreAddress);
1800 }
1801
1802 Value *Cond =
1803 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1804 : Builder.getInt32(1);
1805 Value *NumThreadsArg =
1806 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1807 : Builder.getInt32(-1);
1808
1809 // If this is not a Generic kernel, we can skip generating the wrapper.
1810 Value *WrapperFn;
1811 if (isGenericKernel(*OuterFn))
1812 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1813 else
1814 WrapperFn = Constant::getNullValue(PtrTy);
1815
1816 // Build kmpc_parallel_60 call
1817 Value *Parallel60CallArgs[] = {
1818 /* identifier*/ Ident,
1819 /* global thread num*/ ThreadID,
1820 /* if expression */ Cond,
1821 /* number of threads */ NumThreadsArg,
1822 /* Proc bind */ Builder.getInt32(-1),
1823 /* outlined function */ &OutlinedFn,
1824 /* wrapper function */ WrapperFn,
1825 /* arguments of the outlined funciton*/ Args,
1826 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1827 /* strict for number of threads */ Builder.getInt32(0)};
1828
1829 FunctionCallee RTLFn =
1830 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1831
1832 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1833
1834 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1835 << *Builder.GetInsertBlock()->getParent() << "\n");
1836
1837 // Initialize the local TID stack location with the argument value.
1838 Builder.SetInsertPoint(PrivTID);
1839 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1840 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1841 PrivTIDAddr);
1842
1843 // Remove redundant call to the outlined function.
1844 CI->eraseFromParent();
1845
1846 for (Instruction *I : ToBeDeleted) {
1847 I->eraseFromParent();
1848 }
1849}
1850
1851// Callback used to create OpenMP runtime calls to support
1852// omp parallel clause for the host.
1853// We need to use this callback to replace call to the OutlinedFn in OuterFn
1854// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1855static void
1857 Function *OuterFn, Value *Ident, Value *IfCondition,
1858 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1859 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1860 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1861 FunctionCallee RTLFn;
1862 if (IfCondition) {
1863 RTLFn =
1864 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1865 } else {
1866 RTLFn =
1867 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1868 }
1869 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1870 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1871 LLVMContext &Ctx = F->getContext();
1872 MDBuilder MDB(Ctx);
1873 // Annotate the callback behavior of the __kmpc_fork_call:
1874 // - The callback callee is argument number 2 (microtask).
1875 // - The first two arguments of the callback callee are unknown (-1).
1876 // - All variadic arguments to the __kmpc_fork_call are passed to the
1877 // callback callee.
1878 F->addMetadata(LLVMContext::MD_callback,
1880 2, {-1, -1},
1881 /* VarArgsArePassed */ true)}));
1882 }
1883 }
1884 // Add some known attributes.
1885 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1886 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1887 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1888
1889 assert(OutlinedFn.arg_size() >= 2 &&
1890 "Expected at least tid and bounded tid as arguments");
1891 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1892
1893 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1894 CI->getParent()->setName("omp_parallel");
1895 Builder.SetInsertPoint(CI);
1896
1897 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1898 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1899 &OutlinedFn};
1900
1901 SmallVector<Value *, 16> RealArgs;
1902 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1903 if (IfCondition) {
1904 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1905 RealArgs.push_back(Cond);
1906 }
1907 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1908
1909 // __kmpc_fork_call_if always expects a void ptr as the last argument
1910 // If there are no arguments, pass a null pointer.
1911 auto PtrTy = OMPIRBuilder->VoidPtr;
1912 if (IfCondition && NumCapturedVars == 0) {
1913 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1914 RealArgs.push_back(NullPtrValue);
1915 }
1916
1917 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1918
1919 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1920 << *Builder.GetInsertBlock()->getParent() << "\n");
1921
1922 // Initialize the local TID stack location with the argument value.
1923 Builder.SetInsertPoint(PrivTID);
1924 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1925 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1926 PrivTIDAddr);
1927
1928 // Remove redundant call to the outlined function.
1929 CI->eraseFromParent();
1930
1931 for (Instruction *I : ToBeDeleted) {
1932 I->eraseFromParent();
1933 }
1934}
1935
1937 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1938 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1939 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1940 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1941 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1942
1943 if (!updateToLocation(Loc))
1944 return Loc.IP;
1945
1946 uint32_t SrcLocStrSize;
1947 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1948 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1949 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1950 (ProcBind != OMP_PROC_BIND_default);
1951 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1952 // If we generate code for the target device, we need to allocate
1953 // struct for aggregate params in the device default alloca address space.
1954 // OpenMP runtime requires that the params of the extracted functions are
1955 // passed as zero address space pointers. This flag ensures that extracted
1956 // function arguments are declared in zero address space
1957 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1958
1959 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1960 // only if we compile for host side.
1961 if (NumThreads && !Config.isTargetDevice()) {
1962 Value *Args[] = {
1963 Ident, ThreadID,
1964 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
1966 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
1967 }
1968
1969 if (ProcBind != OMP_PROC_BIND_default) {
1970 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
1971 Value *Args[] = {
1972 Ident, ThreadID,
1973 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
1975 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
1976 }
1977
1978 BasicBlock *InsertBB = Builder.GetInsertBlock();
1979 Function *OuterFn = InsertBB->getParent();
1980
1981 // Save the outer alloca block because the insertion iterator may get
1982 // invalidated and we still need this later.
1983 BasicBlock *OuterAllocaBlock = OuterAllocIP.getBlock();
1984
1985 // Vector to remember instructions we used only during the modeling but which
1986 // we want to delete at the end.
1988
1989 // Change the location to the outer alloca insertion point to create and
1990 // initialize the allocas we pass into the parallel region.
1991 InsertPointTy NewOuter(OuterAllocaBlock, OuterAllocaBlock->begin());
1992 Builder.restoreIP(NewOuter);
1993 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
1994 AllocaInst *ZeroAddrAlloca =
1995 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
1996 Instruction *TIDAddr = TIDAddrAlloca;
1997 Instruction *ZeroAddr = ZeroAddrAlloca;
1998 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
1999 // Add additional casts to enforce pointers in zero address space
2000 TIDAddr = new AddrSpaceCastInst(
2001 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
2002 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
2003 ToBeDeleted.push_back(TIDAddr);
2004 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2005 PointerType ::get(M.getContext(), 0),
2006 "zero.addr.ascast");
2007 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2008 ToBeDeleted.push_back(ZeroAddr);
2009 }
2010
2011 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2012 // associated arguments in the outlined function, so we delete them later.
2013 ToBeDeleted.push_back(TIDAddrAlloca);
2014 ToBeDeleted.push_back(ZeroAddrAlloca);
2015
2016 // Create an artificial insertion point that will also ensure the blocks we
2017 // are about to split are not degenerated.
2018 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2019
2020 BasicBlock *EntryBB = UI->getParent();
2021 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2022 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2023 BasicBlock *PRegPreFiniBB =
2024 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2025 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2026
2027 auto FiniCBWrapper = [&](InsertPointTy IP) {
2028 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2029 // target to the region exit block.
2030 if (IP.getBlock()->end() == IP.getPoint()) {
2032 Builder.restoreIP(IP);
2033 Instruction *I = Builder.CreateBr(PRegExitBB);
2034 IP = InsertPointTy(I->getParent(), I->getIterator());
2035 }
2036 assert(IP.getBlock()->getTerminator()->getNumSuccessors() == 1 &&
2037 IP.getBlock()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2038 "Unexpected insertion point for finalization call!");
2039 return FiniCB(IP);
2040 };
2041
2042 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2043
2044 // Generate the privatization allocas in the block that will become the entry
2045 // of the outlined function.
2046 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2047 InsertPointTy InnerAllocaIP = Builder.saveIP();
2048
2049 AllocaInst *PrivTIDAddr =
2050 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2051 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2052
2053 // Add some fake uses for OpenMP provided arguments.
2054 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2055 Instruction *ZeroAddrUse =
2056 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2057 ToBeDeleted.push_back(ZeroAddrUse);
2058
2059 // EntryBB
2060 // |
2061 // V
2062 // PRegionEntryBB <- Privatization allocas are placed here.
2063 // |
2064 // V
2065 // PRegionBodyBB <- BodeGen is invoked here.
2066 // |
2067 // V
2068 // PRegPreFiniBB <- The block we will start finalization from.
2069 // |
2070 // V
2071 // PRegionExitBB <- A common exit to simplify block collection.
2072 //
2073
2074 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2075
2076 // Let the caller create the body.
2077 assert(BodyGenCB && "Expected body generation callback!");
2078 InsertPointTy CodeGenIP(PRegBodyBB, PRegBodyBB->begin());
2079 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2080 return Err;
2081
2082 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2083
2084 // If OuterFn is a Generic kernel, we need to use device shared memory to
2085 // allocate argument structures. Otherwise, we use stack allocations as usual.
2086 bool UsesDeviceSharedMemory =
2087 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2088 std::unique_ptr<OutlineInfo> OI =
2089 UsesDeviceSharedMemory
2090 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2091 : std::make_unique<OutlineInfo>();
2092
2093 if (Config.isTargetDevice()) {
2094 // Generate OpenMP target specific runtime call
2095 OI->PostOutlineCB = [=, ToBeDeletedVec =
2096 std::move(ToBeDeleted)](Function &OutlinedFn) {
2097 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2098 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2099 ThreadID, ToBeDeletedVec);
2100 };
2101 } else {
2102 // Generate OpenMP host runtime call
2103 OI->PostOutlineCB = [=, ToBeDeletedVec =
2104 std::move(ToBeDeleted)](Function &OutlinedFn) {
2105 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2106 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2107 };
2108 }
2109
2110 OI->FixUpNonEntryAllocas = true;
2111 OI->OuterAllocBB = OuterAllocaBlock;
2112 OI->EntryBB = PRegEntryBB;
2113 OI->ExitBB = PRegExitBB;
2114 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2115 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2116
2117 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2119 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2120
2121 CodeExtractorAnalysisCache CEAC(*OuterFn);
2122 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2123 /* AggregateArgs */ false,
2124 /* BlockFrequencyInfo */ nullptr,
2125 /* BranchProbabilityInfo */ nullptr,
2126 /* AssumptionCache */ nullptr,
2127 /* AllowVarArgs */ true,
2128 /* AllowAlloca */ true,
2129 /* AllocationBlock */ OuterAllocaBlock,
2130 /* DeallocationBlocks */ {},
2131 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2132
2133 // Find inputs to, outputs from the code region.
2134 BasicBlock *CommonExit = nullptr;
2135 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2136 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2137
2138 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2139 /*CollectGlobalInputs=*/true);
2140
2141 Inputs.remove_if([&](Value *I) {
2143 return GV->getValueType() == OpenMPIRBuilder::Ident;
2144
2145 return false;
2146 });
2147
2148 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2149
2150 FunctionCallee TIDRTLFn =
2151 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2152
2153 auto PrivHelper = [&](Value &V) -> Error {
2154 if (&V == TIDAddr || &V == ZeroAddr) {
2155 OI->ExcludeArgsFromAggregate.push_back(&V);
2156 return Error::success();
2157 }
2158
2160 for (Use &U : V.uses())
2161 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2162 if (ParallelRegionBlockSet.count(UserI->getParent()))
2163 Uses.insert(&U);
2164
2165 // __kmpc_fork_call expects extra arguments as pointers. If the input
2166 // already has a pointer type, everything is fine. Otherwise, store the
2167 // value onto stack and load it back inside the to-be-outlined region. This
2168 // will ensure only the pointer will be passed to the function.
2169 // FIXME: if there are more than 15 trailing arguments, they must be
2170 // additionally packed in a struct.
2171 Value *Inner = &V;
2172 if (!V.getType()->isPointerTy()) {
2174 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2175
2176 Builder.restoreIP(OuterAllocIP);
2177 Value *Ptr;
2178 if (UsesDeviceSharedMemory) {
2179 // Use device shared memory instead, if needed.
2180 Ptr = createOMPAllocShared(Builder, V.getType(),
2181 V.getName() + ".reloaded");
2182 for (BasicBlock *DeallocBlock : OuterDeallocBlocks) {
2183 assert(DeallocBlock->getParent() ==
2184 OuterAllocIP.getBlock()->getParent() &&
2185 "Dealloc block must be in the allocation's function to reuse "
2186 "its debug location");
2188 {InsertPointTy(DeallocBlock, DeallocBlock->getFirstInsertionPt()),
2189 Builder.getCurrentDebugLocation()},
2190 Ptr, V.getType());
2191 }
2192 } else {
2193 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2194 V.getName() + ".reloaded");
2195 }
2196
2197 // Store to stack at end of the block that currently branches to the entry
2198 // block of the to-be-outlined region.
2199 Builder.SetInsertPoint(InsertBB,
2200 InsertBB->getTerminator()->getIterator());
2201 Builder.CreateStore(&V, Ptr);
2202
2203 // Load back next to allocations in the to-be-outlined region.
2204 Builder.restoreIP(InnerAllocaIP);
2205 Inner = Builder.CreateLoad(V.getType(), Ptr);
2206 }
2207
2208 Value *ReplacementValue = nullptr;
2209 CallInst *CI = dyn_cast<CallInst>(&V);
2210 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2211 ReplacementValue = PrivTID;
2212 } else {
2213 InsertPointOrErrorTy AfterIP =
2214 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2215 if (!AfterIP)
2216 return AfterIP.takeError();
2217 Builder.restoreIP(*AfterIP);
2218 InnerAllocaIP = {
2219 InnerAllocaIP.getBlock(),
2220 InnerAllocaIP.getBlock()->getTerminator()->getIterator()};
2221
2222 assert(ReplacementValue &&
2223 "Expected copy/create callback to set replacement value!");
2224 if (ReplacementValue == &V)
2225 return Error::success();
2226 }
2227
2228 for (Use *UPtr : Uses)
2229 UPtr->set(ReplacementValue);
2230
2231 return Error::success();
2232 };
2233
2234 // Reset the inner alloca insertion as it will be used for loading the values
2235 // wrapped into pointers before passing them into the to-be-outlined region.
2236 // Configure it to insert immediately after the fake use of zero address so
2237 // that they are available in the generated body and so that the
2238 // OpenMP-related values (thread ID and zero address pointers) remain leading
2239 // in the argument list.
2240 InnerAllocaIP = IRBuilder<>::InsertPoint(
2241 ZeroAddrUse->getParent(), ZeroAddrUse->getNextNode()->getIterator());
2242
2243 // Reset the outer alloca insertion point to the entry of the relevant block
2244 // in case it was invalidated.
2245 OuterAllocIP = IRBuilder<>::InsertPoint(
2246 OuterAllocaBlock, OuterAllocaBlock->getFirstInsertionPt());
2247
2248 for (Value *Input : Inputs) {
2249 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2250 if (Error Err = PrivHelper(*Input))
2251 return Err;
2252 }
2253 LLVM_DEBUG({
2254 for (Value *Output : Outputs)
2255 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2256 });
2257 assert(Outputs.empty() &&
2258 "OpenMP outlining should not produce live-out values!");
2259
2260 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2261 LLVM_DEBUG({
2262 for (auto *BB : Blocks)
2263 dbgs() << " PBR: " << BB->getName() << "\n";
2264 });
2265
2266 // Adjust the finalization stack, verify the adjustment, and call the
2267 // finalize function a last time to finalize values between the pre-fini
2268 // block and the exit block if we left the parallel "the normal way".
2269 auto FiniInfo = FinalizationStack.pop_back_val();
2270 (void)FiniInfo;
2271 assert(FiniInfo.DK == OMPD_parallel &&
2272 "Unexpected finalization stack state!");
2273
2274 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2275
2276 InsertPointTy PreFiniIP(PRegPreFiniBB, PRegPreFiniTI->getIterator());
2277 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2278 if (!FiniBBOrErr)
2279 return FiniBBOrErr.takeError();
2280 {
2282 Builder.restoreIP(PreFiniIP);
2283 Builder.CreateBr(*FiniBBOrErr);
2284 // There's currently a branch to omp.par.exit. Delete it. We will get there
2285 // via the fini block
2286 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2287 Term->eraseFromParent();
2288 }
2289
2290 // Register the outlined info.
2291 addOutlineInfo(std::move(OI));
2292
2293 InsertPointTy AfterIP(UI->getParent(), UI->getParent()->end());
2294 UI->eraseFromParent();
2295
2296 return AfterIP;
2297}
2298
2300 // Build call void __kmpc_flush(ident_t *loc)
2301 uint32_t SrcLocStrSize;
2302 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2303 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2304
2306 Args);
2307}
2308
2310 if (!updateToLocation(Loc))
2311 return;
2312 emitFlush(Loc);
2313}
2314
2316 Value *Message) {
2317 if (!updateToLocation(Loc))
2318 return;
2319
2320 // Build call void __kmpc_error(ident_t *loc, int severity,
2321 // const char *message)
2322 uint32_t SrcLocStrSize;
2323 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2324 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2325 // Severity: 1 = warning, 2 = fatal.
2326 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2327 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2328 Value *Args[] = {Ident, Severity, MessageArg};
2329
2331 Args);
2332}
2333
2335 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2336 uint32_t SrcLocStrSize;
2337 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2338 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2339 Constant *I32Null = ConstantInt::getNullValue(Int32);
2340 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2341
2343 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2344}
2345
2351
2353 const DependData &Dep) {
2354 // Store the pointer to the variable
2355 Value *Addr = Builder.CreateStructGEP(
2356 DependInfo, Entry,
2357 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2358 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2359 Builder.CreateStore(DepValPtr, Addr);
2360 // Store the size of the variable
2361 Value *Size = Builder.CreateStructGEP(
2362 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2363 Builder.CreateStore(
2364 ConstantInt::get(SizeTy,
2365 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2366 Size);
2367 // Store the dependency kind
2368 Value *Flags = Builder.CreateStructGEP(
2369 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2370 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2371 static_cast<unsigned int>(Dep.DepKind)),
2372 Flags);
2373}
2374
2375// Processes the dependencies in Dependencies and does the following
2376// - Allocates space on the stack of an array of DependInfo objects
2377// - Populates each DependInfo object with relevant information of
2378// the corresponding dependence.
2379// - All code is inserted in the entry block of the current function.
2381 OpenMPIRBuilder &OMPBuilder,
2383 // Early return if we have no dependencies to process
2384 if (Dependencies.empty())
2385 return nullptr;
2386
2387 // Given a vector of DependData objects, in this function we create an
2388 // array on the stack that holds kmp_depend_info objects corresponding
2389 // to each dependency. This is then passed to the OpenMP runtime.
2390 // For example, if there are 'n' dependencies then the following psedo
2391 // code is generated. Assume the first dependence is on a variable 'a'
2392 //
2393 // \code{c}
2394 // DepArray = alloc(n x sizeof(kmp_depend_info);
2395 // idx = 0;
2396 // DepArray[idx].base_addr = ptrtoint(&a);
2397 // DepArray[idx].len = 8;
2398 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2399 // ++idx;
2400 // DepArray[idx].base_addr = ...;
2401 // \endcode
2402
2403 IRBuilderBase &Builder = OMPBuilder.Builder;
2404 Type *DependInfo = OMPBuilder.DependInfo;
2405
2406 Value *DepArray = nullptr;
2407 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2408 {
2409 // Use a InsertPointGuard to restore the location back along with the
2410 // insertion point.
2411 IRBuilderBase::InsertPointGuard IPGuard(Builder);
2412 Builder.SetInsertPoint(
2413 Builder.GetInsertBlock()->getParent()->getEntryBlock().getTerminator());
2414 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2415 }
2416
2417 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2418 Value *Base =
2419 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2420 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2421 }
2422 return DepArray;
2423}
2424
2426 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2427 // global_tid);
2428 uint32_t SrcLocStrSize;
2429 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2430 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2431 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2432
2433 // Ignore return result until untied tasks are supported.
2435 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2436}
2437
2439 DependenciesInfo Dependencies) {
2440 if (!updateToLocation(Loc))
2441 return;
2442
2443 Value *DepArray = nullptr;
2444 Type *DepArrayTy = nullptr;
2445 Value *NumDeps = nullptr;
2446 if (Dependencies.DepArray) {
2447 DepArray = Dependencies.DepArray;
2448 NumDeps = Dependencies.NumDeps;
2449 } else if (!Dependencies.Deps.empty()) {
2450 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2451 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2452 {
2454 BasicBlock &entryBB =
2455 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2456 Builder.SetInsertPoint(&entryBB, entryBB.getFirstInsertionPt());
2457 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2458 }
2459
2460 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2461 Value *Base =
2462 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2463 this->emitTaskDependency(Builder, Base, Dep);
2464 }
2465 }
2466
2467 if (DepArray) {
2468 uint32_t SrcLocStrSize;
2469 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2470 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2471 Value *Args[] = {
2472 Ident,
2473 getOrCreateThreadID(Ident),
2474 NumDeps,
2475 DepArray,
2476 ConstantInt::get(Builder.getInt32Ty(), 0),
2478 ConstantInt::get(Builder.getInt32Ty(), false)};
2481 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2482 Args);
2483 } else {
2485 }
2486}
2487
2488/// Create the task duplication function passed to kmpc_taskloop.
2489Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2490 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2491 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2492 if (!DupCB)
2494 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2495
2496 // From OpenMP Runtime p_task_dup_t:
2497 // Routine optionally generated by the compiler for setting the lastprivate
2498 // flag and calling needed constructors for private/firstprivate objects (used
2499 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2500 // lastprivate flag.
2501 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2502
2503 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2504
2505 FunctionType *DupFuncTy = FunctionType::get(
2506 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2507 /*isVarArg=*/false);
2508
2509 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2510 "omp_taskloop_dup", M);
2511 Value *DestTaskArg = DupFunction->getArg(0);
2512 Value *SrcTaskArg = DupFunction->getArg(1);
2513 Value *LastprivateFlagArg = DupFunction->getArg(2);
2514 DestTaskArg->setName("dest_task");
2515 SrcTaskArg->setName("src_task");
2516 LastprivateFlagArg->setName("lastprivate_flag");
2517
2518 IRBuilderBase::InsertPointGuard Guard(Builder);
2519 Builder.SetInsertPoint(
2520 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2521
2522 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2523 Type *TaskWithPrivatesTy =
2524 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2525 Value *TaskPrivates = Builder.CreateGEP(
2526 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2527 Value *ContextPtr = Builder.CreateGEP(
2528 PrivatesTy, TaskPrivates,
2529 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2530 return ContextPtr;
2531 };
2532
2533 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2534 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2535
2536 DestTaskContextPtr->setName("destPtr");
2537 SrcTaskContextPtr->setName("srcPtr");
2538
2539 InsertPointTy AllocaIP(&DupFunction->getEntryBlock(),
2540 DupFunction->getEntryBlock().begin());
2541 InsertPointTy CodeGenIP = Builder.saveIP();
2542 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2543 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2544 if (!AfterIPOrError)
2545 return AfterIPOrError.takeError();
2546 Builder.restoreIP(*AfterIPOrError);
2547
2548 Builder.CreateRetVoid();
2549
2550 return DupFunction;
2551}
2552
2553OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2554 const LocationDescription &Loc, InsertPointTy AllocaIP,
2555 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2556 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2557 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2558 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2559 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2560 Value *TaskContextStructPtrVal, bool FreeAgent) {
2561
2562 if (!updateToLocation(Loc))
2563 return InsertPointTy();
2564
2565 uint32_t SrcLocStrSize;
2566 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2567 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2568
2569 BasicBlock *TaskloopExitBB =
2570 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2571 BasicBlock *TaskloopBodyBB =
2572 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2573 BasicBlock *TaskloopAllocaBB =
2574 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2575
2576 InsertPointTy TaskloopAllocaIP =
2577 InsertPointTy(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2578 InsertPointTy TaskloopBodyIP =
2579 InsertPointTy(TaskloopBodyBB, TaskloopBodyBB->begin());
2580
2581 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2582 return Err;
2583
2584 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2585 if (!result) {
2586 return result.takeError();
2587 }
2588
2589 llvm::CanonicalLoopInfo *CLI = result.get();
2590 auto OI = std::make_unique<OutlineInfo>();
2591 OI->EntryBB = TaskloopAllocaBB;
2592 OI->OuterAllocBB = AllocaIP.getBlock();
2593 OI->ExitBB = TaskloopExitBB;
2594 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2595 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2596
2597 // Add the thread ID argument.
2598 SmallVector<Instruction *> ToBeDeleted;
2599 // dummy instruction to be used as a fake argument
2600 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2601 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2602 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2603 TaskloopAllocaIP, "lb", false, true);
2604 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2605 TaskloopAllocaIP, "ub", false, true);
2606 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2607 TaskloopAllocaIP, "step", false, true);
2608 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2609 // aggregate struct
2610 OI->Inputs.insert(FakeLB);
2611 OI->Inputs.insert(FakeUB);
2612 OI->Inputs.insert(FakeStep);
2613 if (TaskContextStructPtrVal)
2614 OI->Inputs.insert(TaskContextStructPtrVal);
2615 assert(((TaskContextStructPtrVal && DupCB) ||
2616 (!TaskContextStructPtrVal && !DupCB)) &&
2617 "Task context struct ptr and duplication callback must be both set "
2618 "or both null");
2619
2620 // It isn't safe to run the duplication bodygen callback inside the post
2621 // outlining callback so this has to be run now before we know the real task
2622 // shareds structure type.
2623 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2624 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2625 Type *FakeSharedsTy = StructType::get(
2626 Builder.getContext(),
2627 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2628 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2629 FakeSharedsTy,
2630 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2631 if (!TaskDupFnOrErr) {
2632 return TaskDupFnOrErr.takeError();
2633 }
2634 Value *TaskDupFn = *TaskDupFnOrErr;
2635
2636 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2637 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2638 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2639 FakeSharedsTy, Final, Mergeable, Priority,
2640 NumOfCollapseLoops,
2641 FreeAgent](Function &OutlinedFn) mutable {
2642 // Replace the Stale CI by appropriate RTL function call.
2643 assert(OutlinedFn.hasOneUse() &&
2644 "there must be a single user for the outlined function");
2645 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2646
2647 /* Create the casting for the Bounds Values that can be used when outlining
2648 * to replace the uses of the fakes with real values */
2649 BasicBlock *CodeReplBB = StaleCI->getParent();
2650 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2651 Value *CastedLBVal =
2652 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2653 Value *CastedUBVal =
2654 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2655 Value *CastedStepVal =
2656 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2657
2658 Builder.SetInsertPoint(StaleCI);
2659
2660 // Gather the arguments for emitting the runtime call for
2661 // @__kmpc_omp_task_alloc
2662 Function *TaskAllocFn =
2663 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2664
2665 Value *ThreadID = getOrCreateThreadID(Ident);
2666
2667 if (!NoGroup) {
2668 // Emit runtime call for @__kmpc_taskgroup
2669 Function *TaskgroupFn =
2670 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2671 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2672 }
2673
2674 // `flags` Argument Configuration
2675 // Task is tied if (Flags & 1) == 1.
2676 // Task is untied if (Flags & 1) == 0.
2677 // Task is final if (Flags & 2) == 2.
2678 // Task is not final if (Flags & 2) == 0.
2679 // Task is mergeable if (Flags & 4) == 4.
2680 // Task is not mergeable if (Flags & 4) == 0.
2681 // Task is priority if (Flags & 32) == 32.
2682 // Task is not priority if (Flags & 32) == 0.
2683 // Task is free-agent eligible if (Flags & 128) == 128.
2684 // Task is not free-agent eligible if (Flags & 128) == 0.
2685 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2686 if (Final)
2687 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2688 if (Mergeable)
2689 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2690 if (Priority)
2691 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2692 if (FreeAgent)
2693 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
2694
2695 Value *TaskSize = Builder.getInt64(
2696 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2697
2698 AllocaInst *ArgStructAlloca =
2700 assert(ArgStructAlloca &&
2701 "Unable to find the alloca instruction corresponding to arguments "
2702 "for extracted function");
2703 std::optional<TypeSize> ArgAllocSize =
2704 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2705 assert(ArgAllocSize &&
2706 "Unable to determine size of arguments for extracted function");
2707 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2708
2709 // Emit the @__kmpc_omp_task_alloc runtime call
2710 // The runtime call returns a pointer to an area where the task captured
2711 // variables must be copied before the task is run (TaskData)
2712 CallInst *TaskData = Builder.CreateCall(
2713 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2714 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2715 /*task_func=*/&OutlinedFn});
2716
2717 Value *Shareds = StaleCI->getArgOperand(1);
2718 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2719 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2720 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2721 SharedsSize);
2722 // Get the pointer to loop lb, ub, step from task ptr
2723 // and set up the lowerbound,upperbound and step values
2724 llvm::Value *Lb = Builder.CreateGEP(
2725 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2726
2727 llvm::Value *Ub = Builder.CreateGEP(
2728 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2729
2730 llvm::Value *Step = Builder.CreateGEP(
2731 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2732 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2733
2734 // set up the arguments for emitting kmpc_taskloop runtime call
2735 // setting values for ifval, nogroup, sched, grainsize, task_dup
2736 Value *IfCondVal =
2737 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2738 : Builder.getInt32(1);
2739 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2740 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2741 Value *NoGroupVal = Builder.getInt32(1);
2742 Value *SchedVal = Builder.getInt32(Sched);
2743 Value *GrainSizeVal =
2744 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2745 : Builder.getInt64(0);
2746 Value *TaskDup = TaskDupFn;
2747
2748 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2749 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2750
2751 // taskloop runtime call
2752 Function *TaskloopFn =
2753 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2754 Builder.CreateCall(TaskloopFn, Args);
2755
2756 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2757 // nogroup is not defined
2758 if (!NoGroup) {
2759 Function *EndTaskgroupFn =
2760 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2761 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2762 }
2763
2764 StaleCI->eraseFromParent();
2765
2766 Builder.SetInsertPoint(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2767
2768 LoadInst *SharedsOutlined =
2769 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2770 OutlinedFn.getArg(1)->replaceUsesWithIf(
2771 SharedsOutlined,
2772 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2773
2774 Value *IV = CLI->getIndVar();
2775 Type *IVTy = IV->getType();
2776 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2777
2778 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2779 // UpperBound. These GEP's can be reused for loading the tasks respective
2780 // bounds.
2781 Value *TaskLB = nullptr;
2782 Value *TaskUB = nullptr;
2783 Value *TaskStep = nullptr;
2784 Value *LoadTaskLB = nullptr;
2785 Value *LoadTaskUB = nullptr;
2786 Value *LoadTaskStep = nullptr;
2787 for (Instruction &I : *TaskloopAllocaBB) {
2788 if (I.getOpcode() == Instruction::GetElementPtr) {
2789 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2790 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2791 switch (CI->getZExtValue()) {
2792 case 0:
2793 TaskLB = &I;
2794 break;
2795 case 1:
2796 TaskUB = &I;
2797 break;
2798 case 2:
2799 TaskStep = &I;
2800 break;
2801 }
2802 }
2803 } else if (I.getOpcode() == Instruction::Load) {
2804 LoadInst &Load = cast<LoadInst>(I);
2805 if (Load.getPointerOperand() == TaskLB) {
2806 assert(TaskLB != nullptr && "Expected value for TaskLB");
2807 LoadTaskLB = &I;
2808 } else if (Load.getPointerOperand() == TaskUB) {
2809 assert(TaskUB != nullptr && "Expected value for TaskUB");
2810 LoadTaskUB = &I;
2811 } else if (Load.getPointerOperand() == TaskStep) {
2812 assert(TaskStep != nullptr && "Expected value for TaskStep");
2813 LoadTaskStep = &I;
2814 }
2815 }
2816 }
2817
2818 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2819
2820 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2821 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2822 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2823 Value *TripCountMinusOne = Builder.CreateSDiv(
2824 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2825 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2826 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2827 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2828 // set the trip count in the CLI
2829 CLI->setTripCount(CastedTripCount);
2830
2831 Builder.SetInsertPoint(CLI->getBody(),
2832 CLI->getBody()->getFirstInsertionPt());
2833
2834 if (NumOfCollapseLoops > 1) {
2835 llvm::SmallVector<User *> UsersToReplace;
2836 // When using the collapse clause, the bounds of the loop have to be
2837 // adjusted to properly represent the iterator of the outer loop.
2838 Value *IVPlusTaskLB = Builder.CreateAdd(
2839 CLI->getIndVar(),
2840 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2841 // To ensure every Use is correctly captured, we first want to record
2842 // which users to replace the value in, and then replace the value.
2843 for (auto IVUse = CLI->getIndVar()->uses().begin();
2844 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2845 User *IVUser = IVUse->getUser();
2846 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2847 if (Op->getOpcode() == Instruction::URem ||
2848 Op->getOpcode() == Instruction::UDiv) {
2849 UsersToReplace.push_back(IVUser);
2850 }
2851 }
2852 }
2853 for (User *User : UsersToReplace) {
2854 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2855 }
2856 } else {
2857 // The canonical loop is generated with a fixed lower bound. We need to
2858 // update the index calculation code to use the task's lower bound. The
2859 // generated code looks like this:
2860 // %omp_loop.iv = phi ...
2861 // ...
2862 // %tmp = mul [type] %omp_loop.iv, step
2863 // %user_index = add [type] tmp, lb
2864 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2865 // of the normalised induction variable:
2866 // 1. This one: converting the normalised IV to the user IV
2867 // 2. The increment (add)
2868 // 3. The comparison against the trip count (icmp)
2869 // (1) is the only use that is a mul followed by an add so this cannot
2870 // match other IR.
2871 assert(CLI->getIndVar()->getNumUses() == 3 &&
2872 "Canonical loop should have exactly three uses of the ind var");
2873 for (User *IVUser : CLI->getIndVar()->users()) {
2874 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2875 if (Mul->getOpcode() == Instruction::Mul) {
2876 for (User *MulUser : Mul->users()) {
2877 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2878 if (Add->getOpcode() == Instruction::Add) {
2879 Add->setOperand(1, CastedTaskLB);
2880 }
2881 }
2882 }
2883 }
2884 }
2885 }
2886 }
2887
2888 FakeLB->replaceAllUsesWith(CastedLBVal);
2889 FakeUB->replaceAllUsesWith(CastedUBVal);
2890 FakeStep->replaceAllUsesWith(CastedStepVal);
2891 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2892 I->eraseFromParent();
2893 }
2894 };
2895
2896 addOutlineInfo(std::move(OI));
2897 Builder.SetInsertPoint(TaskloopExitBB, TaskloopExitBB->begin());
2898 return Builder.saveIP();
2899}
2900
2903 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2905 llvm::Type::getInt32Ty(M.getContext()));
2906}
2907
2909 const LocationDescription &Loc, InsertPointTy AllocaIP,
2910 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2911 bool Tied, Value *Final, Value *IfCondition,
2912 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2913 bool Mergeable, Value *EventHandle, Value *Priority, bool FreeAgent) {
2914
2915 if (!updateToLocation(Loc))
2916 return InsertPointTy();
2917
2918 uint32_t SrcLocStrSize;
2919 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2920 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2921 // The current basic block is split into four basic blocks. After outlining,
2922 // they will be mapped as follows:
2923 // ```
2924 // def current_fn() {
2925 // current_basic_block:
2926 // br label %task.exit
2927 // task.exit:
2928 // ; instructions after task
2929 // }
2930 // def outlined_fn() {
2931 // task.alloca:
2932 // br label %task.body
2933 // task.body:
2934 // ret void
2935 // }
2936 // ```
2937 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2938 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2939 BasicBlock *TaskAllocaBB =
2940 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2941
2942 InsertPointTy TaskAllocaIP =
2943 InsertPointTy(TaskAllocaBB, TaskAllocaBB->begin());
2944 InsertPointTy TaskBodyIP = InsertPointTy(TaskBodyBB, TaskBodyBB->begin());
2945 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2946 return Err;
2947
2948 auto OI = std::make_unique<OutlineInfo>();
2949 OI->EntryBB = TaskAllocaBB;
2950 OI->OuterAllocBB = AllocaIP.getBlock();
2951 OI->ExitBB = TaskExitBB;
2952 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2953 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2954
2955 // Add the thread ID argument.
2957 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2958 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2959
2960 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2961 Affinities, Mergeable, Priority, EventHandle, FreeAgent,
2962 TaskAllocaBB,
2963 ToBeDeleted](Function &OutlinedFn) mutable {
2964 // Replace the Stale CI by appropriate RTL function call.
2965 assert(OutlinedFn.hasOneUse() &&
2966 "there must be a single user for the outlined function");
2967 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2968
2969 // HasShareds is true if any variables are captured in the outlined region,
2970 // false otherwise.
2971 bool HasShareds = StaleCI->arg_size() > 1;
2972 Builder.SetInsertPoint(StaleCI);
2973
2974 // Gather the arguments for emitting the runtime call for
2975 // @__kmpc_omp_task_alloc
2976 Function *TaskAllocFn =
2977 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2978
2979 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
2980 // call.
2981 Value *ThreadID = getOrCreateThreadID(Ident);
2982
2983 // Argument - `flags`
2984 // Task is tied iff (Flags & 1) == 1.
2985 // Task is untied iff (Flags & 1) == 0.
2986 // Task is final iff (Flags & 2) == 2.
2987 // Task is not final iff (Flags & 2) == 0.
2988 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
2989 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
2990 // Task is detachable iff (Flags & 64) == 64.
2991 // Task is not detachable iff (Flags & 64) == 0.
2992 // Task is priority iff (Flags & 32) == 32.
2993 // Task is not priority iff (Flags & 32) == 0.
2994 // Task is free-agent eligible iff (Flags & 128) == 128.
2995 // Task is not free-agent eligible iff (Flags & 128) == 0.
2996 // TODO: Handle the other flags.
2997 Value *Flags = Builder.getInt32(Tied);
2998 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
2999 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
3000 if (Final) {
3001 Value *FinalFlag =
3002 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
3003 Flags = Builder.CreateOr(FinalFlag, Flags);
3004 }
3005
3006 if (Mergeable || UseMergedIf0Path)
3007 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
3008 if (EventHandle)
3009 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
3010 if (Priority)
3011 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
3012 if (FreeAgent)
3013 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
3014
3015 // Argument - `sizeof_kmp_task_t` (TaskSize)
3016 // Tasksize refers to the size in bytes of kmp_task_t data structure
3017 // including private vars accessed in task.
3018 // TODO: add kmp_task_t_with_privates (privates)
3019 Value *TaskSize = Builder.getInt64(
3020 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3021
3022 // Argument - `sizeof_shareds` (SharedsSize)
3023 // SharedsSize refers to the shareds array size in the kmp_task_t data
3024 // structure.
3025 Value *SharedsSize = Builder.getInt64(0);
3026 if (HasShareds) {
3027 AllocaInst *ArgStructAlloca =
3029 assert(ArgStructAlloca &&
3030 "Unable to find the alloca instruction corresponding to arguments "
3031 "for extracted function");
3032 std::optional<TypeSize> ArgAllocSize =
3033 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3034 assert(ArgAllocSize &&
3035 "Unable to determine size of arguments for extracted function");
3036 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3037 }
3038 // Emit the @__kmpc_omp_task_alloc runtime call
3039 // The runtime call returns a pointer to an area where the task captured
3040 // variables must be copied before the task is run (TaskData)
3042 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3043 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3044 /*task_func=*/&OutlinedFn});
3045
3046 if (Affinities.Count && Affinities.Info) {
3048 OMPRTL___kmpc_omp_reg_task_with_affinity);
3049
3050 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3051 Affinities.Count, Affinities.Info});
3052 }
3053
3054 // Emit detach clause initialization.
3055 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3056 // task_descriptor);
3057 if (EventHandle) {
3059 OMPRTL___kmpc_task_allow_completion_event);
3060 llvm::Value *EventVal =
3061 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3062 llvm::Value *EventHandleAddr =
3063 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3064 Builder.getPtrTy(0));
3065 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3066 Builder.CreateStore(EventVal, EventHandleAddr);
3067 }
3068 // Copy the arguments for outlined function
3069 if (HasShareds) {
3070 Value *Shareds = StaleCI->getArgOperand(1);
3071 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3072 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3073 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3074 SharedsSize);
3075 }
3076
3077 if (Priority) {
3078 //
3079 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3080 // we populate the priority information into the "kmp_task_t" here
3081 //
3082 // The struct "kmp_task_t" definition is available in kmp.h
3083 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3084 // data2 is used for priority
3085 //
3086 Type *Int32Ty = Builder.getInt32Ty();
3087 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3088 // kmp_task_t* => { ptr }
3089 Type *TaskPtr = StructType::get(VoidPtr);
3090 Value *TaskGEP =
3091 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3092 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3093 Type *TaskStructType = StructType::get(
3094 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3095 Value *PriorityData = Builder.CreateInBoundsGEP(
3096 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3097 // kmp_cmplrdata_t => { ptr, ptr }
3098 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3099 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3100 PriorityData, {Zero, Zero});
3101 Builder.CreateStore(Priority, CmplrData);
3102 }
3103
3104 Value *DepArray = nullptr;
3105 Value *NumDeps = nullptr;
3106 if (Dependencies.DepArray) {
3107 DepArray = Dependencies.DepArray;
3108 NumDeps = Dependencies.NumDeps;
3109 } else if (!Dependencies.Deps.empty()) {
3110 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3111 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3112 }
3113
3114 // In the presence of the `if` clause, the following IR is generated:
3115 // ...
3116 // %data = call @__kmpc_omp_task_alloc(...)
3117 // br i1 %if_condition, label %then, label %else
3118 // then:
3119 // call @__kmpc_omp_task(...)
3120 // br label %exit
3121 // else:
3122 // ;; Wait for resolution of dependencies, if any, before
3123 // ;; beginning the task
3124 // call @__kmpc_omp_wait_deps(...)
3125 // call @__kmpc_omp_task_begin_if0(...)
3126 // call @outlined_fn(...)
3127 // call @__kmpc_omp_task_complete_if0(...)
3128 // br label %exit
3129 // exit:
3130 // ...
3131 if (IfCondition && !UseMergedIf0Path) {
3132 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3133 // terminator.
3134 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3135 Instruction *IfTerminator =
3136 Builder.GetInsertPoint()->getParent()->getTerminator();
3137 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3138 Builder.SetInsertPoint(IfTerminator);
3139 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3140 &ElseTI);
3141 Builder.SetInsertPoint(ElseTI);
3142
3143 if (DepArray) {
3144 Function *TaskWaitFn =
3145 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3147 TaskWaitFn,
3148 {Ident, ThreadID, NumDeps, DepArray,
3149 ConstantInt::get(Builder.getInt32Ty(), 0),
3151 }
3152 Function *TaskBeginFn =
3153 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3154 Function *TaskCompleteFn =
3155 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3156 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3157 CallInst *CI = nullptr;
3158 if (HasShareds)
3159 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3160 else
3161 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3162 CI->setDebugLoc(StaleCI->getDebugLoc());
3163 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3164 Builder.SetInsertPoint(ThenTI);
3165 }
3166
3167 if (DepArray) {
3168 Function *TaskFn =
3169 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3171 TaskFn,
3172 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3173 ConstantInt::get(Builder.getInt32Ty(), 0),
3175
3176 } else {
3177 // Emit the @__kmpc_omp_task runtime call to spawn the task
3178 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3179 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3180 }
3181
3182 StaleCI->eraseFromParent();
3183
3184 Builder.SetInsertPoint(TaskAllocaBB, TaskAllocaBB->begin());
3185 if (HasShareds) {
3186 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3187 OutlinedFn.getArg(1)->replaceUsesWithIf(
3188 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3189 }
3190
3191 // The insert point may refer to one of the instructions about to be
3192 // deleted. It is not needed anymore so clear it instead of leaving it
3193 // dangling.
3194 Builder.ClearInsertionPoint();
3195 for (Instruction *I : llvm::reverse(ToBeDeleted))
3196 I->eraseFromParent();
3197 };
3198
3199 addOutlineInfo(std::move(OI));
3200 Builder.SetInsertPoint(TaskExitBB, TaskExitBB->begin());
3201
3202 return Builder.saveIP();
3203}
3204
3206 const LocationDescription &Loc, InsertPointTy AllocaIP,
3207 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3208 if (!updateToLocation(Loc))
3209 return InsertPointTy();
3210
3211 uint32_t SrcLocStrSize;
3212 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3213 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3214 Value *ThreadID = getOrCreateThreadID(Ident);
3215
3216 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3217 Function *TaskgroupFn =
3218 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3219 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3220
3221 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3222 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3223 return Err;
3224
3225 Builder.SetInsertPoint(TaskgroupExitBB);
3226 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3227 Function *EndTaskgroupFn =
3228 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3229 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3230
3231 return Builder.saveIP();
3232}
3233
3235 const LocationDescription &Loc, InsertPointTy AllocaIP,
3237 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3238 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3239
3240 if (!updateToLocation(Loc))
3241 return Loc.IP;
3242
3243 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3244
3245 // Each section is emitted as a switch case
3246 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3247 // -> OMP.createSection() which generates the IR for each section
3248 // Iterate through all sections and emit a switch construct:
3249 // switch (IV) {
3250 // case 0:
3251 // <SectionStmt[0]>;
3252 // break;
3253 // ...
3254 // case <NumSection> - 1:
3255 // <SectionStmt[<NumSection> - 1]>;
3256 // break;
3257 // }
3258 // ...
3259 // section_loop.after:
3260 // <FiniCB>;
3261 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3262 Builder.restoreIP(CodeGenIP);
3264 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3265 Function *CurFn = Continue->getParent();
3266 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3267
3268 unsigned CaseNumber = 0;
3269 for (auto SectionCB : SectionCBs) {
3271 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3272 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3273 Builder.SetInsertPoint(CaseBB);
3274 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3275 if (Error Err =
3276 SectionCB(InsertPointTy(),
3277 {CaseEndBr->getParent(), CaseEndBr->getIterator()}, {}))
3278 return Err;
3279 CaseNumber++;
3280 }
3281 // remove the existing terminator from body BB since there can be no
3282 // terminators after switch/case
3283 return Error::success();
3284 };
3285 // Loop body ends here
3286 // LowerBound, UpperBound, and STride for createCanonicalLoop
3287 Type *I32Ty = Type::getInt32Ty(M.getContext());
3288 Value *LB = ConstantInt::get(I32Ty, 0);
3289 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3290 Value *ST = ConstantInt::get(I32Ty, 1);
3292 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3293 if (!LoopInfo)
3294 return LoopInfo.takeError();
3295
3296 InsertPointOrErrorTy WsloopIP =
3297 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3298 WorksharingLoopType::ForStaticLoop, !IsNowait);
3299 if (!WsloopIP)
3300 return WsloopIP.takeError();
3301 InsertPointTy AfterIP = *WsloopIP;
3302
3303 BasicBlock *LoopFini = AfterIP.getBlock()->getSinglePredecessor();
3304 assert(LoopFini && "Bad structure of static workshare loop finalization");
3305
3306 // Apply the finalization callback in LoopAfterBB
3307 auto FiniInfo = FinalizationStack.pop_back_val();
3308 assert(FiniInfo.DK == OMPD_sections &&
3309 "Unexpected finalization stack state!");
3310 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3311 return Err;
3312
3313 return AfterIP;
3314}
3315
3318 BodyGenCallbackTy BodyGenCB,
3319 FinalizeCallbackTy FiniCB) {
3320 if (!updateToLocation(Loc))
3321 return Loc.IP;
3322
3323 auto FiniCBWrapper = [&](InsertPointTy IP) {
3324 if (IP.getBlock()->end() != IP.getPoint())
3325 return FiniCB(IP);
3326 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3327 // will fail because that function requires the Finalization Basic Block to
3328 // have a terminator, which is already removed by EmitOMPRegionBody.
3329 // IP is currently at cancelation block.
3330 // We need to backtrack to the condition block to fetch
3331 // the exit block and create a branch from cancelation
3332 // to exit block.
3334 Builder.restoreIP(IP);
3335 auto *CaseBB = Loc.IP.getBlock();
3336 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3337 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3338 Instruction *I = Builder.CreateBr(ExitBB);
3339 IP = InsertPointTy(I->getParent(), I->getIterator());
3340 return FiniCB(IP);
3341 };
3342
3343 Directive OMPD = Directive::OMPD_sections;
3344 // Since we are using Finalization Callback here, HasFinalize
3345 // and IsCancellable have to be true
3346 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3347 /*Conditional*/ false, /*hasFinalize*/ true,
3348 /*IsCancellable*/ true);
3349}
3350
3356
3357Value *OpenMPIRBuilder::getGPUThreadID() {
3360 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3361 {});
3362}
3363
3364Value *OpenMPIRBuilder::getGPUWarpSize() {
3366 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3367}
3368
3369Value *OpenMPIRBuilder::getNVPTXWarpID() {
3370 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3371 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3372}
3373
3374Value *OpenMPIRBuilder::getNVPTXLaneID() {
3375 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3376 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3377 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3378 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3379 "nvptx_lane_id");
3380}
3381
3382Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3383 Type *ToType) {
3384 Type *FromType = From->getType();
3385 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3386 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3387 assert(FromSize > 0 && "From size must be greater than zero");
3388 assert(ToSize > 0 && "To size must be greater than zero");
3389 if (FromType == ToType)
3390 return From;
3391 if (FromSize == ToSize)
3392 return Builder.CreateBitCast(From, ToType);
3393 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3394 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3395 InsertPointTy SaveIP = Builder.saveIP();
3396 Builder.restoreIP(AllocaIP);
3397 Value *CastItem = Builder.CreateAlloca(ToType);
3398 Builder.restoreIP(SaveIP);
3399
3400 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3401 CastItem, Builder.getPtrTy(0));
3402 Builder.CreateStore(From, ValCastItem);
3403 return Builder.CreateLoad(ToType, CastItem);
3404}
3405
3406Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3407 Value *Element,
3408 Type *ElementType,
3409 Value *Offset) {
3410 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3411 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3412
3413 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3414 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3415 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3416 Value *WarpSize =
3417 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3419 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3420 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3421 Value *WarpSizeCast =
3422 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3423 Value *ShuffleCall =
3424 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3425 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3426 // down to the requested element type, otherwise storing the result would
3427 // write past the end of an element narrower than the shuffle width.
3428 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3429}
3430
3431void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3432 Value *DstAddr, Type *ElemType,
3433 Value *Offset, Type *ReductionArrayTy,
3434 bool IsByRefElem) {
3435 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3436 // Create the loop over the big sized data.
3437 // ptr = (void*)Elem;
3438 // ptrEnd = (void*) Elem + 1;
3439 // Step = 8;
3440 // while (ptr + Step < ptrEnd)
3441 // shuffle((int64_t)*ptr);
3442 // Step = 4;
3443 // while (ptr + Step < ptrEnd)
3444 // shuffle((int32_t)*ptr);
3445 // ...
3446 Type *IndexTy = Builder.getIndexTy(
3447 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3448 Value *ElemPtr = DstAddr;
3449 Value *Ptr = SrcAddr;
3450 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3451 if (Size < IntSize)
3452 continue;
3453 Type *IntType = Builder.getIntNTy(IntSize * 8);
3454 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3455 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3456 Value *SrcAddrGEP =
3457 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3458 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3459 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3460
3461 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3462 if ((Size / IntSize) > 1) {
3463 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3464 SrcAddrGEP, Builder.getPtrTy());
3465 BasicBlock *PreCondBB =
3466 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3467 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3468 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3469 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3470 emitBlock(PreCondBB, CurFunc);
3471 PHINode *PhiSrc =
3472 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3473 PhiSrc->addIncoming(Ptr, CurrentBB);
3474 PHINode *PhiDest =
3475 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3476 PhiDest->addIncoming(ElemPtr, CurrentBB);
3477 Ptr = PhiSrc;
3478 ElemPtr = PhiDest;
3479 Value *PtrDiff = Builder.CreatePtrDiff(
3480 Builder.getInt8Ty(), PtrEnd,
3481 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3482 Builder.CreateCondBr(
3483 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3484 ExitBB);
3485 emitBlock(ThenBB, CurFunc);
3486 Value *Res = createRuntimeShuffleFunction(
3487 AllocaIP,
3488 Builder.CreateAlignedLoad(
3489 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3490 IntType, Offset);
3491 Builder.CreateAlignedStore(Res, ElemPtr,
3492 M.getDataLayout().getPrefTypeAlign(ElemType));
3493 Value *LocalPtr =
3494 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3495 Value *LocalElemPtr =
3496 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3497 PhiSrc->addIncoming(LocalPtr, ThenBB);
3498 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3499 emitBranch(PreCondBB);
3500 emitBlock(ExitBB, CurFunc);
3501 } else {
3502 // The shuffled value comes back as the chunk's integer type, so the
3503 // store covers exactly this chunk regardless of what ElemType is.
3504 Value *Res = createRuntimeShuffleFunction(
3505 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3506 Builder.CreateStore(Res, ElemPtr);
3507 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3508 ElemPtr =
3509 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3510 }
3511 Size = Size % IntSize;
3512 }
3513}
3514
3515Error OpenMPIRBuilder::emitReductionListCopy(
3516 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3517 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3518 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3519 Type *IndexTy = Builder.getIndexTy(
3520 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3521 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3522
3523 // Iterates, element-by-element, through the source Reduce list and
3524 // make a copy.
3525 for (auto En : enumerate(ReductionInfos)) {
3526 const ReductionInfo &RI = En.value();
3527 Value *SrcElementAddr = nullptr;
3528 AllocaInst *DestAlloca = nullptr;
3529 Value *DestElementAddr = nullptr;
3530 Value *DestElementPtrAddr = nullptr;
3531 // Should we shuffle in an element from a remote lane?
3532 bool ShuffleInElement = false;
3533 // Set to true to update the pointer in the dest Reduce list to a
3534 // newly created element.
3535 bool UpdateDestListPtr = false;
3536
3537 // Step 1.1: Get the address for the src element in the Reduce list.
3538 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3539 ReductionArrayTy, SrcBase,
3540 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3541 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3542
3543 // Step 1.2: Create a temporary to store the element in the destination
3544 // Reduce list.
3545 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3546 ReductionArrayTy, DestBase,
3547 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3548 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3549 switch (Action) {
3551 InsertPointTy CurIP = Builder.saveIP();
3552 Builder.restoreIP(AllocaIP);
3553
3554 Type *DestAllocaType =
3555 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3556 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3557 ".omp.reduction.element");
3558 DestAlloca->setAlignment(
3559 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3560 DestElementAddr = DestAlloca;
3561 DestElementAddr =
3562 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3563 DestElementAddr->getName() + ".ascast");
3564 Builder.restoreIP(CurIP);
3565 ShuffleInElement = true;
3566 UpdateDestListPtr = true;
3567 break;
3568 }
3570 DestElementAddr =
3571 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3572 break;
3573 }
3574 }
3575
3576 // Now that all active lanes have read the element in the
3577 // Reduce list, shuffle over the value from the remote lane.
3578 if (ShuffleInElement) {
3579 Type *ShuffleType = RI.ElementType;
3580 Value *ShuffleSrcAddr = SrcElementAddr;
3581 Value *ShuffleDestAddr = DestElementAddr;
3582 AllocaInst *LocalStorage = nullptr;
3583
3584 if (IsByRefElem) {
3585 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3586 assert(RI.ByRefAllocatedType &&
3587 "Expected by-ref allocated type to be set");
3588 // For by-ref reductions, we need to copy from the remote lane the
3589 // actual value of the partial reduction computed by that remote lane;
3590 // rather than, for example, a pointer to that data or, even worse, a
3591 // pointer to the descriptor of the by-ref reduction element.
3592 ShuffleType = RI.ByRefElementType;
3593
3594 if (RI.DataPtrPtrGen) {
3595 // Descriptor-based by-ref: extract data pointer from descriptor.
3596 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3597 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3598
3599 if (!GenResult)
3600 return GenResult.takeError();
3601
3602 ShuffleSrcAddr =
3603 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3604
3605 {
3606 InsertPointTy OldIP = Builder.saveIP();
3607 Builder.restoreIP(AllocaIP);
3608
3609 LocalStorage = Builder.CreateAlloca(ShuffleType);
3610 Builder.restoreIP(OldIP);
3611 ShuffleDestAddr = LocalStorage;
3612 }
3613 } else {
3614 // Non-descriptor by-ref: the pointer already references data
3615 // directly. Shuffle into the destination alloca.
3616 ShuffleDestAddr = DestElementAddr;
3617 }
3618 }
3619
3620 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3621 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3622
3623 if (IsByRefElem && RI.DataPtrPtrGen) {
3624 // Copy descriptor from source and update base_ptr to shuffled data
3625 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3626 DestAlloca, Builder.getPtrTy(), ".ascast");
3627
3628 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3629 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3630 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3631
3632 if (!GenResult)
3633 return GenResult.takeError();
3634 }
3635 } else {
3636 switch (RI.EvaluationKind) {
3637 case EvalKind::Scalar: {
3638 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3639 // Store the source element value to the dest element address.
3640 Builder.CreateStore(Elem, DestElementAddr);
3641 break;
3642 }
3643 case EvalKind::Complex: {
3644 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3645 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3646 Value *SrcReal = Builder.CreateLoad(
3647 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3648 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3649 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3650 Value *SrcImg = Builder.CreateLoad(
3651 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3652
3653 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3654 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3655 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3656 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3657 Builder.CreateStore(SrcReal, DestRealPtr);
3658 Builder.CreateStore(SrcImg, DestImgPtr);
3659 break;
3660 }
3661 case EvalKind::Aggregate: {
3662 Value *SizeVal = Builder.getInt64(
3663 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3664 Builder.CreateMemCpy(
3665 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3666 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3667 SizeVal, false);
3668 break;
3669 }
3670 };
3671 }
3672
3673 // Step 3.1: Modify reference in dest Reduce list as needed.
3674 // Modifying the reference in Reduce list to point to the newly
3675 // created element. The element is live in the current function
3676 // scope and that of functions it invokes (i.e., reduce_function).
3677 // RemoteReduceData[i] = (void*)&RemoteElem
3678 if (UpdateDestListPtr) {
3679 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3680 DestElementAddr, Builder.getPtrTy(),
3681 DestElementAddr->getName() + ".ascast");
3682 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3683 }
3684 }
3685
3686 return Error::success();
3687}
3688
3689Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3690 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3691 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3692 IRBuilder<>::InsertPointGuard IPG(Builder);
3693 LLVMContext &Ctx = M.getContext();
3694 FunctionType *FuncTy = FunctionType::get(
3695 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3696 /* IsVarArg */ false);
3697 Function *WcFunc =
3699 "_omp_reduction_inter_warp_copy_func", &M);
3700 WcFunc->setCallingConv(Config.getRuntimeCC());
3701 WcFunc->setAttributes(FuncAttrs);
3702 WcFunc->addParamAttr(0, Attribute::NoUndef);
3703 WcFunc->addParamAttr(1, Attribute::NoUndef);
3704 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3705 Builder.SetInsertPoint(EntryBB);
3706 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3707
3708 // ReduceList: thread local Reduce list.
3709 // At the stage of the computation when this function is called, partially
3710 // aggregated values reside in the first lane of every active warp.
3711 Argument *ReduceListArg = WcFunc->getArg(0);
3712 // NumWarps: number of warps active in the parallel region. This could
3713 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3714 Argument *NumWarpsArg = WcFunc->getArg(1);
3715
3716 // This array is used as a medium to transfer, one reduce element at a time,
3717 // the data from the first lane of every warp to lanes in the first warp
3718 // in order to perform the final step of a reduction in a parallel region
3719 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3720 // for reduced latency, as well as to have a distinct copy for concurrently
3721 // executing target regions. The array is declared with common linkage so
3722 // as to be shared across compilation units.
3723 StringRef TransferMediumName =
3724 "__openmp_nvptx_data_transfer_temporary_storage";
3725 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3726 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3727 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3728 if (!TransferMedium) {
3729 TransferMedium = new GlobalVariable(
3730 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3731 UndefValue::get(ArrayTy), TransferMediumName,
3732 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3733 /*AddressSpace=*/3);
3734 }
3735
3736 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3737 Value *GPUThreadID = getGPUThreadID();
3738 // nvptx_lane_id = nvptx_id % warpsize
3739 Value *LaneID = getNVPTXLaneID();
3740 // nvptx_warp_id = nvptx_id / warpsize
3741 Value *WarpID = getNVPTXWarpID();
3742
3743 InsertPointTy AllocaIP =
3744 InsertPointTy(Builder.GetInsertBlock(),
3745 Builder.GetInsertBlock()->getFirstInsertionPt());
3746 Type *Arg0Type = ReduceListArg->getType();
3747 Type *Arg1Type = NumWarpsArg->getType();
3748 Builder.restoreIP(AllocaIP);
3749 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3750 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3751 AllocaInst *NumWarpsAlloca =
3752 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3753 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3754 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3755 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3756 NumWarpsAlloca, Builder.getPtrTy(0),
3757 NumWarpsAlloca->getName() + ".ascast");
3758 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3759 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3760 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3761 InsertPointTy CodeGenIP =
3762 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3763 Builder.restoreIP(CodeGenIP);
3764
3765 Value *ReduceList =
3766 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3767
3768 for (auto En : enumerate(ReductionInfos)) {
3769 //
3770 // Warp master copies reduce element to transfer medium in __shared__
3771 // memory.
3772 //
3773 const ReductionInfo &RI = En.value();
3774 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3775 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3776 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3777 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3778 Type *CType = Builder.getIntNTy(TySize * 8);
3779
3780 unsigned NumIters = RealTySize / TySize;
3781 if (NumIters == 0)
3782 continue;
3783 Value *Cnt = nullptr;
3784 Value *CntAddr = nullptr;
3785 BasicBlock *PrecondBB = nullptr;
3786 BasicBlock *ExitBB = nullptr;
3787 if (NumIters > 1) {
3788 CodeGenIP = Builder.saveIP();
3789 Builder.restoreIP(AllocaIP);
3790 CntAddr =
3791 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3792
3793 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3794 CntAddr->getName() + ".ascast");
3795 Builder.restoreIP(CodeGenIP);
3796 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3797 CntAddr,
3798 /*Volatile=*/false);
3799 PrecondBB = BasicBlock::Create(Ctx, "precond");
3800 ExitBB = BasicBlock::Create(Ctx, "exit");
3801 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3802 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3803 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3804 /*Volatile=*/false);
3805 Value *Cmp = Builder.CreateICmpULT(
3806 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3807 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3808 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3809 }
3810
3811 // kmpc_barrier.
3812 InsertPointOrErrorTy BarrierIP1 =
3814 omp::Directive::OMPD_unknown,
3815 /* ForceSimpleCall */ false,
3816 /* CheckCancelFlag */ true);
3817 if (!BarrierIP1)
3818 return BarrierIP1.takeError();
3819 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3820 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3821 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3822
3823 // if (lane_id == 0)
3824 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3825 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3826 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3827
3828 // Reduce element = LocalReduceList[i]
3829 auto *RedListArrayTy =
3830 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3831 Type *IndexTy = Builder.getIndexTy(
3832 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3833 Value *ElemPtrPtr =
3834 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3835 {ConstantInt::get(IndexTy, 0),
3836 ConstantInt::get(IndexTy, En.index())});
3837 // elemptr = ((CopyType*)(elemptrptr)) + I
3838 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3839
3840 if (IsByRefElem && RI.DataPtrPtrGen) {
3841 InsertPointOrErrorTy GenRes =
3842 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3843
3844 if (!GenRes)
3845 return GenRes.takeError();
3846
3847 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3848 }
3849
3850 if (NumIters > 1)
3851 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3852
3853 // Get pointer to location in transfer medium.
3854 // MediumPtr = &medium[warp_id]
3855 Value *MediumPtr = Builder.CreateInBoundsGEP(
3856 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3857 // elem = *elemptr
3858 //*MediumPtr = elem
3859 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3860 // Store the source element value to the dest element address.
3861 Builder.CreateStore(Elem, MediumPtr,
3862 /*IsVolatile*/ true);
3863 Builder.CreateBr(MergeBB);
3864
3865 // else
3866 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3867 Builder.CreateBr(MergeBB);
3868
3869 // endif
3870 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3871 InsertPointOrErrorTy BarrierIP2 =
3873 omp::Directive::OMPD_unknown,
3874 /* ForceSimpleCall */ false,
3875 /* CheckCancelFlag */ true);
3876 if (!BarrierIP2)
3877 return BarrierIP2.takeError();
3878
3879 // Warp 0 copies reduce element from transfer medium
3880 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3881 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3882 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3883
3884 Value *NumWarpsVal =
3885 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3886 // Up to 32 threads in warp 0 are active.
3887 Value *IsActiveThread =
3888 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3889 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3890
3891 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3892
3893 // SecMediumPtr = &medium[tid]
3894 // SrcMediumVal = *SrcMediumPtr
3895 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3896 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3897 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3898 Value *TargetElemPtrPtr =
3899 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3900 {ConstantInt::get(IndexTy, 0),
3901 ConstantInt::get(IndexTy, En.index())});
3902 Value *TargetElemPtrVal =
3903 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3904 Value *TargetElemPtr = TargetElemPtrVal;
3905
3906 if (IsByRefElem && RI.DataPtrPtrGen) {
3907 InsertPointOrErrorTy GenRes =
3908 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3909
3910 if (!GenRes)
3911 return GenRes.takeError();
3912
3913 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3914 }
3915
3916 if (NumIters > 1)
3917 TargetElemPtr =
3918 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3919
3920 // *TargetElemPtr = SrcMediumVal;
3921 Value *SrcMediumValue =
3922 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3923 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3924 Builder.CreateBr(W0MergeBB);
3925
3926 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3927 Builder.CreateBr(W0MergeBB);
3928
3929 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3930
3931 if (NumIters > 1) {
3932 Cnt = Builder.CreateNSWAdd(
3933 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3934 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3935
3936 auto *CurFn = Builder.GetInsertBlock()->getParent();
3937 emitBranch(PrecondBB);
3938 emitBlock(ExitBB, CurFn);
3939 }
3940 RealTySize %= TySize;
3941 }
3942 }
3943
3944 Builder.CreateRetVoid();
3945
3946 return WcFunc;
3947}
3948
3949Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3950 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3951 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3952 LLVMContext &Ctx = M.getContext();
3953 IRBuilder<>::InsertPointGuard IPG(Builder);
3954 FunctionType *FuncTy =
3955 FunctionType::get(Builder.getVoidTy(),
3956 {Builder.getPtrTy(), Builder.getInt16Ty(),
3957 Builder.getInt16Ty(), Builder.getInt16Ty()},
3958 /* IsVarArg */ false);
3959 Function *SarFunc =
3961 "_omp_reduction_shuffle_and_reduce_func", &M);
3962 SarFunc->setCallingConv(Config.getRuntimeCC());
3963 SarFunc->setAttributes(FuncAttrs);
3964 SarFunc->addParamAttr(0, Attribute::NoUndef);
3965 SarFunc->addParamAttr(1, Attribute::NoUndef);
3966 SarFunc->addParamAttr(2, Attribute::NoUndef);
3967 SarFunc->addParamAttr(3, Attribute::NoUndef);
3968 SarFunc->addParamAttr(1, Attribute::SExt);
3969 SarFunc->addParamAttr(2, Attribute::SExt);
3970 SarFunc->addParamAttr(3, Attribute::SExt);
3971 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3972 Builder.SetInsertPoint(EntryBB);
3973 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3974
3975 // Thread local Reduce list used to host the values of data to be reduced.
3976 Argument *ReduceListArg = SarFunc->getArg(0);
3977 // Current lane id; could be logical.
3978 Argument *LaneIDArg = SarFunc->getArg(1);
3979 // Offset of the remote source lane relative to the current lane.
3980 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
3981 // Algorithm version. This is expected to be known at compile time.
3982 Argument *AlgoVerArg = SarFunc->getArg(3);
3983
3984 Type *ReduceListArgType = ReduceListArg->getType();
3985 Type *LaneIDArgType = LaneIDArg->getType();
3986 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
3987 Value *ReduceListAlloca = Builder.CreateAlloca(
3988 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
3989 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3990 LaneIDArg->getName() + ".addr");
3991 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
3992 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
3993 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3994 AlgoVerArg->getName() + ".addr");
3995 ArrayType *RedListArrayTy =
3996 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3997
3998 // Create a local thread-private variable to host the Reduce list
3999 // from a remote lane.
4000 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
4001 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
4002
4003 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4004 ReduceListAlloca, ReduceListArgType,
4005 ReduceListAlloca->getName() + ".ascast");
4006 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4007 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
4008 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4009 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
4010 RemoteLaneOffsetAlloca->getName() + ".ascast");
4011 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4012 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
4013 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4014 RemoteReductionListAlloca, Builder.getPtrTy(),
4015 RemoteReductionListAlloca->getName() + ".ascast");
4016
4017 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
4018 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
4019 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
4020 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
4021
4022 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
4023 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4024 Value *RemoteLaneOffset =
4025 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4026 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4027
4028 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4029
4030 // This loop iterates through the list of reduce elements and copies,
4031 // element by element, from a remote lane in the warp to RemoteReduceList,
4032 // hosted on the thread's stack.
4033 Error EmitRedLsCpRes = emitReductionListCopy(
4034 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4035 ReduceList, RemoteListAddrCast, IsByRef,
4036 {RemoteLaneOffset, nullptr, nullptr});
4037
4038 if (EmitRedLsCpRes)
4039 return EmitRedLsCpRes;
4040
4041 // The actions to be performed on the Remote Reduce list is dependent
4042 // on the algorithm version.
4043 //
4044 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4045 // LaneId % 2 == 0 && Offset > 0):
4046 // do the reduction value aggregation
4047 //
4048 // The thread local variable Reduce list is mutated in place to host the
4049 // reduced data, which is the aggregated value produced from local and
4050 // remote lanes.
4051 //
4052 // Note that AlgoVer is expected to be a constant integer known at compile
4053 // time.
4054 // When AlgoVer==0, the first conjunction evaluates to true, making
4055 // the entire predicate true during compile time.
4056 // When AlgoVer==1, the second conjunction has only the second part to be
4057 // evaluated during runtime. Other conjunctions evaluates to false
4058 // during compile time.
4059 // When AlgoVer==2, the third conjunction has only the second part to be
4060 // evaluated during runtime. Other conjunctions evaluates to false
4061 // during compile time.
4062 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4063 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4064 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4065 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4066 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4067 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4068 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4069 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4070 Value *RemoteOffsetComp =
4071 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4072 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4073 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4074 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4075
4076 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4077 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4078 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4079
4080 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4081 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4082 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4083 ReduceList, Builder.getPtrTy());
4084 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4085 RemoteListAddrCast, Builder.getPtrTy());
4086 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4087 ->addFnAttr(Attribute::NoUnwind);
4088 Builder.CreateBr(MergeBB);
4089
4090 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4091 Builder.CreateBr(MergeBB);
4092
4093 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4094
4095 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4096 // Reduce list.
4097 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4098 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4099 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4100
4101 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4102 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4103 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4104 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4105
4106 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4107
4108 EmitRedLsCpRes = emitReductionListCopy(
4109 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4110 RemoteListAddrCast, ReduceList, IsByRef);
4111
4112 if (EmitRedLsCpRes)
4113 return EmitRedLsCpRes;
4114
4115 Builder.CreateBr(CpyMergeBB);
4116
4117 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4118 Builder.CreateBr(CpyMergeBB);
4119
4120 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4121
4122 Builder.CreateRetVoid();
4123
4124 return SarFunc;
4125}
4126
4128OpenMPIRBuilder::generateReductionDescriptor(
4129 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4130 Type *DescriptorType,
4131 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4132 DataPtrPtrGen) {
4133
4134 // Copy the source descriptor to preserve all metadata (rank, extents,
4135 // strides, etc.)
4136 Value *DescriptorSize =
4137 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4138 Builder.CreateMemCpy(
4139 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4140 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4141 DescriptorSize);
4142
4143 // Update the base pointer field to point to the local shuffled data
4144 Value *DataPtrField;
4145 InsertPointOrErrorTy GenResult =
4146 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4147
4148 if (!GenResult)
4149 return GenResult.takeError();
4150
4151 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4152 DataPtr, Builder.getPtrTy(), ".ascast"),
4153 DataPtrField);
4154
4155 return Builder.saveIP();
4156}
4157
4158Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4159 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4160 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4161 InsertPointTy OldIP = Builder.saveIP();
4162 Builder.restoreIP(AllocaIP);
4163
4164 AllocaInst *DescriptorAlloca =
4165 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4166 DescriptorAlloca->setAlignment(
4167 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4168 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4169 DescriptorAlloca, DescriptorPtrTy,
4170 DescriptorAlloca->getName() + ".ascast");
4171
4172 Builder.restoreIP(OldIP);
4173
4174 InsertPointOrErrorTy GenResult =
4175 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4176 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4177 if (!GenResult)
4178 return GenResult.takeError();
4179
4180 return DescriptorAddr;
4181}
4182
4183Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4184 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4185 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4186 IRBuilder<>::InsertPointGuard IPG(Builder);
4187 LLVMContext &Ctx = M.getContext();
4188 FunctionType *FuncTy = FunctionType::get(
4189 Builder.getVoidTy(),
4190 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4191 /* IsVarArg */ false);
4192 Function *LtGCFunc =
4194 "_omp_reduction_list_to_global_copy_func", &M);
4195 LtGCFunc->setAttributes(FuncAttrs);
4196 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4197 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4198 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4199
4200 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4201 Builder.SetInsertPoint(EntryBlock);
4202 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4203
4204 // Buffer: global reduction buffer.
4205 Argument *BufferArg = LtGCFunc->getArg(0);
4206 // Idx: index of the buffer.
4207 Argument *IdxArg = LtGCFunc->getArg(1);
4208 // ReduceList: thread local Reduce list.
4209 Argument *ReduceListArg = LtGCFunc->getArg(2);
4210
4211 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4212 BufferArg->getName() + ".addr");
4213 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4214 IdxArg->getName() + ".addr");
4215 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4216 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4217 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4218 BufferArgAlloca, Builder.getPtrTy(),
4219 BufferArgAlloca->getName() + ".ascast");
4220 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4221 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4222 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4223 ReduceListArgAlloca, Builder.getPtrTy(),
4224 ReduceListArgAlloca->getName() + ".ascast");
4225
4226 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4227 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4228 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4229
4230 Value *LocalReduceList =
4231 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4232 Value *BufferArgVal =
4233 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4234 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4235 Type *IndexTy = Builder.getIndexTy(
4236 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4237 for (auto En : enumerate(ReductionInfos)) {
4238 const ReductionInfo &RI = En.value();
4239 auto *RedListArrayTy =
4240 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4241 // Reduce element = LocalReduceList[i]
4242 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4243 RedListArrayTy, LocalReduceList,
4244 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4245 // elemptr = ((CopyType*)(elemptrptr)) + I
4246 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4247
4248 // Global = Buffer.VD[Idx];
4249 Value *BufferVD =
4250 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4251 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4252 ReductionsBufferTy, BufferVD, 0, En.index());
4253
4254 switch (RI.EvaluationKind) {
4255 case EvalKind::Scalar: {
4256 Value *TargetElement;
4257
4258 if (IsByRef.empty() || !IsByRef[En.index()]) {
4259 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4260 } else {
4261 if (RI.DataPtrPtrGen) {
4262 InsertPointOrErrorTy GenResult =
4263 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4264
4265 if (!GenResult)
4266 return GenResult.takeError();
4267
4268 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4269 }
4270 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4271 }
4272
4273 Builder.CreateStore(TargetElement, GlobVal);
4274 break;
4275 }
4276 case EvalKind::Complex: {
4277 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4278 RI.ElementType, ElemPtr, 0, 0, ".realp");
4279 Value *SrcReal = Builder.CreateLoad(
4280 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4281 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4282 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4283 Value *SrcImg = Builder.CreateLoad(
4284 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4285
4286 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4287 RI.ElementType, GlobVal, 0, 0, ".realp");
4288 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4289 RI.ElementType, GlobVal, 0, 1, ".imagp");
4290 Builder.CreateStore(SrcReal, DestRealPtr);
4291 Builder.CreateStore(SrcImg, DestImgPtr);
4292 break;
4293 }
4294 case EvalKind::Aggregate: {
4295 Value *SizeVal =
4296 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4297 Builder.CreateMemCpy(
4298 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4299 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4300 break;
4301 }
4302 }
4303 }
4304
4305 Builder.CreateRetVoid();
4306 return LtGCFunc;
4307}
4308
4309Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4310 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4311 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4312 IRBuilder<>::InsertPointGuard IPG(Builder);
4313 LLVMContext &Ctx = M.getContext();
4314 FunctionType *FuncTy = FunctionType::get(
4315 Builder.getVoidTy(),
4316 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4317 /* IsVarArg */ false);
4318 Function *LtGRFunc =
4320 "_omp_reduction_list_to_global_reduce_func", &M);
4321 LtGRFunc->setAttributes(FuncAttrs);
4322 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4323 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4324 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4325
4326 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4327 Builder.SetInsertPoint(EntryBlock);
4328 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4329
4330 // Buffer: global reduction buffer.
4331 Argument *BufferArg = LtGRFunc->getArg(0);
4332 // Idx: index of the buffer.
4333 Argument *IdxArg = LtGRFunc->getArg(1);
4334 // ReduceList: thread local Reduce list.
4335 Argument *ReduceListArg = LtGRFunc->getArg(2);
4336
4337 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4338 BufferArg->getName() + ".addr");
4339 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4340 IdxArg->getName() + ".addr");
4341 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4342 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4343 auto *RedListArrayTy =
4344 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4345
4346 // 1. Build a list of reduction variables.
4347 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4348 Value *LocalReduceList =
4349 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4350
4351 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4352
4353 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4354 BufferArgAlloca, Builder.getPtrTy(),
4355 BufferArgAlloca->getName() + ".ascast");
4356 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4357 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4358 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4359 ReduceListArgAlloca, Builder.getPtrTy(),
4360 ReduceListArgAlloca->getName() + ".ascast");
4361 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4362 LocalReduceList, Builder.getPtrTy(),
4363 LocalReduceList->getName() + ".ascast");
4364
4365 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4366 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4367 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4368
4369 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4370 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4371 Type *IndexTy = Builder.getIndexTy(
4372 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4373 for (auto En : enumerate(ReductionInfos)) {
4374 const ReductionInfo &RI = En.value();
4375
4376 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4377 RedListArrayTy, LocalReduceListAddrCast,
4378 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4379 Value *BufferVD =
4380 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4381 // Global = Buffer.VD[Idx];
4382 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4383 ReductionsBufferTy, BufferVD, 0, En.index());
4384
4385 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4386 // Get source descriptor from the reduce list argument
4387 Value *ReduceList =
4388 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4389 Value *SrcElementPtrPtr =
4390 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4391 {ConstantInt::get(IndexTy, 0),
4392 ConstantInt::get(IndexTy, En.index())});
4393 Value *SrcDescriptorAddr =
4394 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4395
4396 // Copy descriptor from source and update base_ptr to global buffer data
4397 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4398 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4399 if (!ByRefAlloc)
4400 return ByRefAlloc.takeError();
4401
4402 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4403 } else {
4404 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4405 }
4406 }
4407
4408 // Call reduce_function(GlobalReduceList, ReduceList)
4409 Value *ReduceList =
4410 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4411 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4412 ->addFnAttr(Attribute::NoUnwind);
4413 Builder.CreateRetVoid();
4414 return LtGRFunc;
4415}
4416
4417Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4418 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4419 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4420 IRBuilder<>::InsertPointGuard IPG(Builder);
4421 LLVMContext &Ctx = M.getContext();
4422 FunctionType *FuncTy = FunctionType::get(
4423 Builder.getVoidTy(),
4424 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4425 /* IsVarArg */ false);
4426 Function *GtLCFunc =
4428 "_omp_reduction_global_to_list_copy_func", &M);
4429 GtLCFunc->setAttributes(FuncAttrs);
4430 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4431 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4432 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4433
4434 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4435 Builder.SetInsertPoint(EntryBlock);
4436 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4437
4438 // Buffer: global reduction buffer.
4439 Argument *BufferArg = GtLCFunc->getArg(0);
4440 // Idx: index of the buffer.
4441 Argument *IdxArg = GtLCFunc->getArg(1);
4442 // ReduceList: thread local Reduce list.
4443 Argument *ReduceListArg = GtLCFunc->getArg(2);
4444
4445 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4446 BufferArg->getName() + ".addr");
4447 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4448 IdxArg->getName() + ".addr");
4449 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4450 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4451 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4452 BufferArgAlloca, Builder.getPtrTy(),
4453 BufferArgAlloca->getName() + ".ascast");
4454 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4455 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4456 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4457 ReduceListArgAlloca, Builder.getPtrTy(),
4458 ReduceListArgAlloca->getName() + ".ascast");
4459 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4460 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4461 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4462
4463 Value *LocalReduceList =
4464 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4465 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4466 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4467 Type *IndexTy = Builder.getIndexTy(
4468 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4469 for (auto En : enumerate(ReductionInfos)) {
4470 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4471 auto *RedListArrayTy =
4472 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4473 // Reduce element = LocalReduceList[i]
4474 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4475 RedListArrayTy, LocalReduceList,
4476 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4477 // elemptr = ((CopyType*)(elemptrptr)) + I
4478 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4479 // Global = Buffer.VD[Idx];
4480 Value *BufferVD =
4481 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4482 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4483 ReductionsBufferTy, BufferVD, 0, En.index());
4484
4485 switch (RI.EvaluationKind) {
4486 case EvalKind::Scalar: {
4487 Type *ElemType = RI.ElementType;
4488
4489 if (!IsByRef.empty() && IsByRef[En.index()]) {
4490 ElemType = RI.ByRefElementType;
4491 if (RI.DataPtrPtrGen) {
4492 InsertPointOrErrorTy GenResult =
4493 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4494
4495 if (!GenResult)
4496 return GenResult.takeError();
4497
4498 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4499 }
4500 }
4501
4502 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4503 Builder.CreateStore(TargetElement, ElemPtr);
4504 break;
4505 }
4506 case EvalKind::Complex: {
4507 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4508 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4509 Value *SrcReal = Builder.CreateLoad(
4510 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4511 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4512 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4513 Value *SrcImg = Builder.CreateLoad(
4514 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4515
4516 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4517 RI.ElementType, ElemPtr, 0, 0, ".realp");
4518 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4519 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4520 Builder.CreateStore(SrcReal, DestRealPtr);
4521 Builder.CreateStore(SrcImg, DestImgPtr);
4522 break;
4523 }
4524 case EvalKind::Aggregate: {
4525 Value *SizeVal =
4526 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4527 Builder.CreateMemCpy(
4528 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4529 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4530 SizeVal, false);
4531 break;
4532 }
4533 }
4534 }
4535
4536 Builder.CreateRetVoid();
4537 return GtLCFunc;
4538}
4539
4540Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4541 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4542 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4543 IRBuilder<>::InsertPointGuard IPG(Builder);
4544 LLVMContext &Ctx = M.getContext();
4545 auto *FuncTy = FunctionType::get(
4546 Builder.getVoidTy(),
4547 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4548 /* IsVarArg */ false);
4549 Function *GtLRFunc =
4551 "_omp_reduction_global_to_list_reduce_func", &M);
4552 GtLRFunc->setAttributes(FuncAttrs);
4553 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4554 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4555 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4556
4557 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4558 Builder.SetInsertPoint(EntryBlock);
4559 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4560
4561 // Buffer: global reduction buffer.
4562 Argument *BufferArg = GtLRFunc->getArg(0);
4563 // Idx: index of the buffer.
4564 Argument *IdxArg = GtLRFunc->getArg(1);
4565 // ReduceList: thread local Reduce list.
4566 Argument *ReduceListArg = GtLRFunc->getArg(2);
4567
4568 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4569 BufferArg->getName() + ".addr");
4570 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4571 IdxArg->getName() + ".addr");
4572 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4573 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4574 ArrayType *RedListArrayTy =
4575 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4576
4577 // 1. Build a list of reduction variables.
4578 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4579 Value *LocalReduceList =
4580 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4581
4582 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4583
4584 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4585 BufferArgAlloca, Builder.getPtrTy(),
4586 BufferArgAlloca->getName() + ".ascast");
4587 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4588 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4589 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4590 ReduceListArgAlloca, Builder.getPtrTy(),
4591 ReduceListArgAlloca->getName() + ".ascast");
4592 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4593 LocalReduceList, Builder.getPtrTy(),
4594 LocalReduceList->getName() + ".ascast");
4595
4596 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4597 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4598 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4599
4600 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4601 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4602 Type *IndexTy = Builder.getIndexTy(
4603 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4604 for (auto En : enumerate(ReductionInfos)) {
4605 const ReductionInfo &RI = En.value();
4606
4607 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4608 RedListArrayTy, ReductionList,
4609 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4610 // Global = Buffer.VD[Idx];
4611 Value *BufferVD =
4612 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4613 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4614 ReductionsBufferTy, BufferVD, 0, En.index());
4615
4616 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4617 // Get source descriptor from the reduce list
4618 Value *ReduceListVal =
4619 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4620 Value *SrcElementPtrPtr =
4621 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4622 {ConstantInt::get(IndexTy, 0),
4623 ConstantInt::get(IndexTy, En.index())});
4624 Value *SrcDescriptorAddr =
4625 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4626
4627 // Copy descriptor from source and update base_ptr to global buffer data
4628 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4629 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4630 if (!ByRefAlloc)
4631 return ByRefAlloc.takeError();
4632
4633 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4634 } else {
4635 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4636 }
4637 }
4638
4639 // Call reduce_function(ReduceList, GlobalReduceList)
4640 Value *ReduceList =
4641 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4642 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4643 ->addFnAttr(Attribute::NoUnwind);
4644 Builder.CreateRetVoid();
4645 return GtLRFunc;
4646}
4647
4648std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4649 std::string Suffix =
4650 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4651 return (Name + Suffix).str();
4652}
4653
4654Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4655 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4657 AttributeList FuncAttrs) {
4658 IRBuilder<>::InsertPointGuard IPG(Builder);
4659 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4660 {Builder.getPtrTy(), Builder.getPtrTy()},
4661 /* IsVarArg */ false);
4662 std::string Name = getReductionFuncName(ReducerName);
4663 Function *ReductionFunc =
4665 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4666 ReductionFunc->setAttributes(FuncAttrs);
4667 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4668 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4669 BasicBlock *EntryBB =
4670 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4671 Builder.SetInsertPoint(EntryBB);
4672 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4673
4674 // Need to alloca memory here and deal with the pointers before getting
4675 // LHS/RHS pointers out
4676 Value *LHSArrayPtr = nullptr;
4677 Value *RHSArrayPtr = nullptr;
4678 Argument *Arg0 = ReductionFunc->getArg(0);
4679 Argument *Arg1 = ReductionFunc->getArg(1);
4680 Type *Arg0Type = Arg0->getType();
4681 Type *Arg1Type = Arg1->getType();
4682
4683 Value *LHSAlloca =
4684 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4685 Value *RHSAlloca =
4686 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4687 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4688 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4689 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4690 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4691 Builder.CreateStore(Arg0, LHSAddrCast);
4692 Builder.CreateStore(Arg1, RHSAddrCast);
4693 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4694 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4695
4696 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4697 Type *IndexTy = Builder.getIndexTy(
4698 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4699 SmallVector<Value *> LHSPtrs, RHSPtrs;
4700 for (auto En : enumerate(ReductionInfos)) {
4701 const ReductionInfo &RI = En.value();
4702 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4703 RedArrayTy, RHSArrayPtr,
4704 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4705 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4706 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4707 RHSI8Ptr, RI.PrivateVariable->getType(),
4708 RHSI8Ptr->getName() + ".ascast");
4709
4710 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4711 RedArrayTy, LHSArrayPtr,
4712 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4713 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4714 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4715 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4716
4718 LHSPtrs.emplace_back(LHSPtr);
4719 RHSPtrs.emplace_back(RHSPtr);
4720 } else {
4721 Value *LHS = LHSPtr;
4722 Value *RHS = RHSPtr;
4723
4724 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4725 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4726 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4727 }
4728
4729 Value *Reduced;
4730 InsertPointOrErrorTy AfterIP =
4731 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4732 if (!AfterIP)
4733 return AfterIP.takeError();
4734 if (!Builder.GetInsertBlock())
4735 return ReductionFunc;
4736
4737 Builder.restoreIP(*AfterIP);
4738
4739 if (!IsByRef.empty() && !IsByRef[En.index()])
4740 Builder.CreateStore(Reduced, LHSPtr);
4741 }
4742 }
4743
4745 for (auto En : enumerate(ReductionInfos)) {
4746 unsigned Index = En.index();
4747 const ReductionInfo &RI = En.value();
4748 Value *LHSFixupPtr, *RHSFixupPtr;
4749 Builder.restoreIP(RI.ReductionGenClang(
4750 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4751
4752 // Fix the CallBack code genereated to use the correct Values for the LHS
4753 // and RHS
4754 LHSFixupPtr->replaceUsesWithIf(
4755 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4756 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4757 ReductionFunc;
4758 });
4759 RHSFixupPtr->replaceUsesWithIf(
4760 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4761 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4762 ReductionFunc;
4763 });
4764 }
4765
4766 Builder.CreateRetVoid();
4767 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4768 // to the entry block (this is dones for higher opt levels by later passes in
4769 // the pipeline). This has caused issues because non-entry `alloca`s force the
4770 // function to use dynamic stack allocations and we might run out of scratch
4771 // memory.
4772 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4773
4774 return ReductionFunc;
4775}
4776
4777static void
4779 bool IsGPU) {
4780 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4781 (void)RI;
4782 assert(RI.Variable && "expected non-null variable");
4783 assert(RI.PrivateVariable && "expected non-null private variable");
4784 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4785 "expected non-null reduction generator callback");
4786 if (!IsGPU) {
4787 assert(
4788 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4789 "expected variables and their private equivalents to have the same "
4790 "type");
4791 }
4792 assert(RI.Variable->getType()->isPointerTy() &&
4793 "expected variables to be pointers");
4794 }
4795}
4796
4797// The atomic cross-team reduction fast path applies when every reduction in the
4798// set can be represented by an atomicrmw. Clang only populates it for scalar
4799// reductions with a supported atomic operator.
4802 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4803 return static_cast<bool>(RI.AtomicReductionGen);
4804 });
4805}
4806
4808 const LocationDescription &Loc, InsertPointTy AllocaIP,
4809 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4810 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4811 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4812 Value *SrcLocInfo) {
4813 if (!updateToLocation(Loc))
4814 return InsertPointTy();
4815 Builder.restoreIP(CodeGenIP);
4816 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4817 LLVMContext &Ctx = M.getContext();
4818
4819 // Source location for the ident struct
4820 if (!SrcLocInfo) {
4821 uint32_t SrcLocStrSize;
4822 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4823 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4824 }
4825
4826 if (ReductionInfos.size() == 0)
4827 return Builder.saveIP();
4828
4829 BasicBlock *ContinuationBlock = nullptr;
4831 // Copied code from createReductions
4832 BasicBlock *InsertBlock = Loc.IP.getBlock();
4833 ContinuationBlock =
4834 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
4835 InsertBlock->getTerminator()->eraseFromParent();
4836 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
4837 }
4838
4839 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4840 AttributeList FuncAttrs;
4841 AttrBuilder AttrBldr(Ctx);
4842 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4843 AttrBldr.addAttribute(Attr);
4844 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4845 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4846
4847 CodeGenIP = Builder.saveIP();
4848 Expected<Function *> ReductionResult = createReductionFunction(
4849 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4850 ReductionGenCBKind, FuncAttrs);
4851 if (!ReductionResult)
4852 return ReductionResult.takeError();
4853 Function *ReductionFunc = *ReductionResult;
4854 Builder.restoreIP(CodeGenIP);
4855
4856 // Set the grid value in the config needed for lowering later on
4857 if (GridValue.has_value())
4858 Config.setGridValue(GridValue.value());
4859 else
4860 Config.setGridValue(getGridValue(T, ReductionFunc));
4861
4862 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4863 // RedList, shuffle_reduce_func, interwarp_copy_func);
4864 // or
4865 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4866 Value *Res;
4867
4868 // 1. Build a list of reduction variables.
4869 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4870 auto Size = ReductionInfos.size();
4871 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4872 Type *FuncPtrTy =
4873 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4874 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4875 CodeGenIP = Builder.saveIP();
4876 Builder.restoreIP(AllocaIP);
4877 Value *ReductionListAlloca =
4878 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4879 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4880 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4881 Builder.restoreIP(CodeGenIP);
4882 Type *IndexTy = Builder.getIndexTy(
4883 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4884 for (auto En : enumerate(ReductionInfos)) {
4885 const ReductionInfo &RI = En.value();
4886 Value *ElemPtr = Builder.CreateInBoundsGEP(
4887 RedArrayTy, ReductionList,
4888 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4889
4890 Value *PrivateVar = RI.PrivateVariable;
4891 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4892 if (IsByRefElem)
4893 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4894
4895 Value *CastElem =
4896 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4897 Builder.CreateStore(CastElem, ElemPtr);
4898 }
4899 CodeGenIP = Builder.saveIP();
4900 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4901 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4902
4903 if (!SarFunc)
4904 return SarFunc.takeError();
4905
4906 Expected<Function *> CopyResult =
4907 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4908 if (!CopyResult)
4909 return CopyResult.takeError();
4910 Function *WcFunc = *CopyResult;
4911 Builder.restoreIP(CodeGenIP);
4912
4913 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4914
4915 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4916 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4917 // not currently use it. It is computed here conservatively as max(element
4918 // sizes) * N rather than the exact sum, which over-calculates the size for
4919 // mixed reduction types but is harmless given the argument is unused.
4920 // TODO: Consider dropping this computation if the runtime API is ever revised
4921 // to remove the unused parameter.
4922 unsigned MaxDataSize = 0;
4923 SmallVector<Type *> ReductionTypeArgs;
4924 for (auto En : enumerate(ReductionInfos)) {
4925 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4926 // the actual data size stored in the global reduction buffer, consistent
4927 // with the ReductionsBufferTy struct used for GEP offsets below.
4928 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4929 ? En.value().ByRefElementType
4930 : En.value().ElementType;
4931 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4932 if (Size > MaxDataSize)
4933 MaxDataSize = Size;
4934 ReductionTypeArgs.emplace_back(RedTypeArg);
4935 }
4936 Value *ReductionDataSize =
4937 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4938
4939 // Helper function to copy thread-local data back to the original reduction
4940 // list.
4941 Function *CopyScratchToListFunc = nullptr;
4942 // Thread-local storage for the reduction variables.
4943 Value *ScratchForCopyBack = nullptr;
4944 // RL pointer to which the final value from the per-thread scratch should be
4945 // copied back. (Basically RL, appropriately casted if necessary.)
4946 Value *RLForCopyBack = RL;
4947
4948 bool IsAtomicReduction =
4949 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4950
4951 if (!IsTeamsReduction) {
4952 Value *SarFuncCast =
4953 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4954 Value *WcFuncCast =
4955 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4956 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4957 WcFuncCast};
4959 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4960 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4961 } else if (IsAtomicReduction) {
4962 // Atomic cross-team reduction fast path: determine the team's main thread
4963 // that is later to fold its value atomically into the mapped variable.
4964 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4965 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4966 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4967 } else {
4968 CodeGenIP = Builder.saveIP();
4969 StructType *ReductionsBufferTy = StructType::create(
4970 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4971
4972 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4973 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4974 if (!LtGCFunc)
4975 return LtGCFunc.takeError();
4976
4977 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
4978 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4979 if (!GtLCFunc)
4980 return GtLCFunc.takeError();
4981
4982 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
4983 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
4984 if (!GtLRFunc)
4985 return GtLRFunc.takeError();
4986
4987 Builder.restoreIP(CodeGenIP);
4988
4989 // The runtime's cross-team final aggregate uses the storage pointed at by
4990 // its reduce-list argument as per-thread scratch. When the surrounding
4991 // kernel is already in SPMD execution mode, clang emitted each reduction
4992 // private as a per-thread `alloca addrspace(5)`, so the original red_list
4993 // (RL) is already per-thread and nothing else is needed.
4994 //
4995 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
4996 // Generic-mode globalization put the reduction private into team-shared
4997 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
4998 // point all threads of the last team would race on the shared LDS slot.
4999 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
5000 // value in, and hand the per-thread RL to the runtime instead. The writer
5001 // thread copies the final value from that per-thread scratch back to RL
5002 // before running the existing combine path below.
5003
5004 // Thread-local RL (might need localization below before being passed to the
5005 // runtime).
5006 Value *RuntimeRL = RL;
5007
5008 if (!IsSPMD) {
5009 CodeGenIP = Builder.saveIP();
5010 Builder.restoreIP(AllocaIP);
5011 // Allocate thread-local buffer for the reduction variables.
5012 Value *PerThreadScratchAlloca = Builder.CreateAlloca(
5013 ReductionsBufferTy, /*ArraySize=*/nullptr, ".omp.reduction.scratch");
5014 Value *PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
5015 PerThreadScratchAlloca, PtrTy,
5016 PerThreadScratchAlloca->getName() + ".ascast");
5017 // Allocate thread-local buffer for the pointers to the reduction
5018 // variables.
5019 Value *PerThreadRedListAlloca =
5020 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
5021 ".omp.reduction.per_thread_red_list");
5022 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
5023 PerThreadRedListAlloca, PtrTy,
5024 PerThreadRedListAlloca->getName() + ".ascast");
5025 Builder.restoreIP(CodeGenIP);
5026
5027 // Iterate over the reduction variables and copy the team-local value to
5028 // the thread-local buffer.
5029 for (auto En : enumerate(ReductionInfos)) {
5030 const ReductionInfo &RI = En.value();
5031 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5032
5033 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5034 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5035 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5036 0, En.index());
5037
5038 Value *RuntimeListEntry = FieldPtr;
5039 if (IsByRefElem && RI.DataPtrPtrGen) {
5040 Value *SrcDescriptor =
5041 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5042 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5043 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5044 if (!Descriptor)
5045 return Descriptor.takeError();
5046 RuntimeListEntry = *Descriptor;
5047 }
5048 Builder.CreateStore(RuntimeListEntry, Slot);
5049 }
5050 // The copy helpers were emitted with default-AS (AS 0) pointer params
5051 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5052 // but PerThreadScratch and RL live in the target's default AS, which
5053 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5054 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5055 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5056 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5057 PerThreadScratch, CopyArg0Ty);
5058 RLForCopyBack =
5059 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5060 // Use index 0 because there is no array of target values to index into,
5061 // there is only one thread-local memory slot.
5062 // restoreIP above left a stale/empty debug location; this inlinable call
5063 // to a debug-info-bearing helper needs one or the verifier rejects the
5064 // module ("!dbg attachment points at wrong subprogram") after inlining.
5065 Builder.SetCurrentDebugLocation(Loc.DL);
5066 Builder.CreateCall(
5067 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5068 CopyScratchToListFunc = *GtLCFunc;
5069 }
5070
5071 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5072 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5073
5074 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5075 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5076 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5077 }
5078
5079 // 5. Build if (res == 1)
5080 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5081 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5082 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5083 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5084
5085 // 6. Build then branch: where we have reduced values in the master
5086 // thread in each team.
5087 // __kmpc_end_reduce{_nowait}(<gtid>);
5088 // break;
5089 emitBlock(ThenBB, CurFunc);
5090
5091 // Copy the writer thread's per-thread scratch result back into the original
5092 // red-list storage before the existing combine path reads RI.PrivateVariable.
5093 // Set a debug location: this inlinable call to a debug-info-bearing helper
5094 // needs one or the verifier rejects the module after inlining.
5095 if (ScratchForCopyBack) {
5096 Builder.SetCurrentDebugLocation(Loc.DL);
5097 Builder.CreateCall(
5098 CopyScratchToListFunc,
5099 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5100 }
5101
5102 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5103 for (auto En : enumerate(ReductionInfos)) {
5104 const ReductionInfo &RI = En.value();
5105
5106 // Atomic cross-team fast path: each team's main thread folds its
5107 // team-reduced value directly into the mapped reduction variable with a
5108 // single atomicrmw.
5109 if (IsAtomicReduction) {
5111 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5112 if (!AfterIP)
5113 return AfterIP.takeError();
5114 Builder.restoreIP(*AfterIP);
5115 continue;
5116 }
5117
5119 Value *RedValue = RI.Variable;
5120
5121 Value *RHS =
5122 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5123
5125 Value *LHSPtr, *RHSPtr;
5126 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5127 &LHSPtr, &RHSPtr, CurFunc));
5128
5129 // Fix the CallBack code genereated to use the correct Values for the LHS
5130 // and RHS. Cast to match types before replacing (necessary to handle
5131 // different address spaces).
5132 if (LHSPtr->getType() != RedValue->getType())
5133 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5134 RedValue, LHSPtr->getType());
5135 if (RHSPtr->getType() != RHS->getType())
5136 RHS =
5137 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5138
5139 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5140 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5141 ReductionFunc;
5142 });
5143 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5144 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5145 ReductionFunc;
5146 });
5147 } else {
5148 if (IsByRef.empty() || !IsByRef[En.index()]) {
5149 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5150 "red.value." + Twine(En.index()));
5151 }
5152 Value *PrivateRedValue = Builder.CreateLoad(
5153 ValueType, RHS, "red.private.value" + Twine(En.index()));
5154 Value *Reduced;
5155 InsertPointOrErrorTy AfterIP =
5156 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5157 if (!AfterIP)
5158 return AfterIP.takeError();
5159 Builder.restoreIP(*AfterIP);
5160
5161 if (!IsByRef.empty() && !IsByRef[En.index()])
5162 Builder.CreateStore(Reduced, RI.Variable);
5163 }
5164 }
5165 emitBlock(ExitBB, CurFunc);
5166 if (ContinuationBlock) {
5167 Builder.CreateBr(ContinuationBlock);
5168 Builder.SetInsertPoint(ContinuationBlock);
5169 }
5170 Config.setEmitLLVMUsed();
5171
5172 return Builder.saveIP();
5173}
5174
5176 Type *VoidTy = Type::getVoidTy(M.getContext());
5177 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5178 auto *FuncTy =
5179 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5181 ".omp.reduction.func", &M);
5182}
5183
5185 Function *ReductionFunc,
5187 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5188 IRBuilder<>::InsertPointGuard IPG(Builder);
5189 Module *Module = ReductionFunc->getParent();
5190 BasicBlock *ReductionFuncBlock =
5191 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5192 Builder.SetInsertPoint(ReductionFuncBlock);
5193 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5194 Value *LHSArrayPtr = nullptr;
5195 Value *RHSArrayPtr = nullptr;
5196 if (IsGPU) {
5197 // Need to alloca memory here and deal with the pointers before getting
5198 // LHS/RHS pointers out
5199 //
5200 Argument *Arg0 = ReductionFunc->getArg(0);
5201 Argument *Arg1 = ReductionFunc->getArg(1);
5202 Type *Arg0Type = Arg0->getType();
5203 Type *Arg1Type = Arg1->getType();
5204
5205 Value *LHSAlloca =
5206 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5207 Value *RHSAlloca =
5208 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5209 Value *LHSAddrCast =
5210 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5211 Value *RHSAddrCast =
5212 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5213 Builder.CreateStore(Arg0, LHSAddrCast);
5214 Builder.CreateStore(Arg1, RHSAddrCast);
5215 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5216 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5217 } else {
5218 LHSArrayPtr = ReductionFunc->getArg(0);
5219 RHSArrayPtr = ReductionFunc->getArg(1);
5220 }
5221
5222 unsigned NumReductions = ReductionInfos.size();
5223 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5224
5225 for (auto En : enumerate(ReductionInfos)) {
5226 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5227 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5228 RedArrayTy, LHSArrayPtr, 0, En.index());
5229 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5230 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5231 LHSI8Ptr, RI.Variable->getType());
5232 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5233 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5234 RedArrayTy, RHSArrayPtr, 0, En.index());
5235 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5236 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5237 RHSI8Ptr, RI.PrivateVariable->getType());
5238 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5239 Value *Reduced;
5241 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5242 if (!AfterIP)
5243 return AfterIP.takeError();
5244
5245 Builder.restoreIP(*AfterIP);
5246 // TODO: Consider flagging an error.
5247 if (!Builder.GetInsertBlock())
5248 return Error::success();
5249
5250 // store is inside of the reduction region when using by-ref
5251 if (!IsByRef[En.index()])
5252 Builder.CreateStore(Reduced, LHSPtr);
5253 }
5254 Builder.CreateRetVoid();
5255 return Error::success();
5256}
5257
5259 const LocationDescription &Loc, InsertPointTy AllocaIP,
5260 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5261 bool IsNoWait, bool IsTeamsReduction) {
5262 assert(ReductionInfos.size() == IsByRef.size());
5263 if (Config.isGPU())
5264 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5265 IsByRef, IsNoWait, IsTeamsReduction);
5266
5267 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5268
5269 if (!updateToLocation(Loc))
5270 return InsertPointTy();
5271
5272 if (ReductionInfos.size() == 0)
5273 return Builder.saveIP();
5274
5275 BasicBlock *InsertBlock = Loc.IP.getBlock();
5276 BasicBlock *ContinuationBlock =
5277 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
5278 InsertBlock->getTerminator()->eraseFromParent();
5279
5280 // Create and populate array of type-erased pointers to private reduction
5281 // values.
5282 unsigned NumReductions = ReductionInfos.size();
5283 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5284 Builder.SetInsertPoint(AllocaIP.getBlock()->getTerminator());
5285 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5286
5287 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
5288 // Emitting the alloca moved the insertion point into the alloca block and
5289 // can clear the debug loc. Restore back to Loc.DL.
5290 Builder.SetCurrentDebugLocation(Loc.DL);
5291
5292 for (auto En : enumerate(ReductionInfos)) {
5293 unsigned Index = En.index();
5294 const ReductionInfo &RI = En.value();
5295 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5296 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5297 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5298 }
5299
5300 // Emit a call to the runtime function that orchestrates the reduction.
5301 // Declare the reduction function in the process.
5302 Type *IndexTy = Builder.getIndexTy(
5303 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5304 Function *Func = Builder.GetInsertBlock()->getParent();
5305 Module *Module = Func->getParent();
5306 uint32_t SrcLocStrSize;
5307 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5308 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5309 return RI.AtomicReductionGen;
5310 });
5311 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5312 CanGenerateAtomic
5313 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5314 : IdentFlag(0));
5315 Value *ThreadId = getOrCreateThreadID(Ident);
5316 Constant *NumVariables = Builder.getInt32(NumReductions);
5317 const DataLayout &DL = Module->getDataLayout();
5318 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5319 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5320 Function *ReductionFunc = getFreshReductionFunc(*Module);
5321 Value *Lock = getOMPCriticalRegionLock(".reduction");
5323 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5324 : RuntimeFunction::OMPRTL___kmpc_reduce);
5325 CallInst *ReduceCall =
5326 createRuntimeFunctionCall(ReduceFunc,
5327 {Ident, ThreadId, NumVariables, RedArraySize,
5328 RedArray, ReductionFunc, Lock},
5329 "reduce");
5330
5331 // Create final reduction entry blocks for the atomic and non-atomic case.
5332 // Emit IR that dispatches control flow to one of the blocks based on the
5333 // reduction supporting the atomic mode.
5334 BasicBlock *NonAtomicRedBlock =
5335 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5336 BasicBlock *AtomicRedBlock =
5337 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5338 SwitchInst *Switch =
5339 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5340 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5341 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5342
5343 // Populate the non-atomic reduction using the elementwise reduction function.
5344 // This loads the elements from the global and private variables and reduces
5345 // them before storing back the result to the global variable.
5346 Builder.SetInsertPoint(NonAtomicRedBlock);
5347 for (auto En : enumerate(ReductionInfos)) {
5348 const ReductionInfo &RI = En.value();
5350 // We have one less load for by-ref case because that load is now inside of
5351 // the reduction region
5352 Value *RedValue = RI.Variable;
5353 if (!IsByRef[En.index()]) {
5354 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5355 "red.value." + Twine(En.index()));
5356 }
5357 Value *PrivateRedValue =
5358 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5359 "red.private.value." + Twine(En.index()));
5360 Value *Reduced;
5361 InsertPointOrErrorTy AfterIP =
5362 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5363 if (!AfterIP)
5364 return AfterIP.takeError();
5365 Builder.restoreIP(*AfterIP);
5366
5367 if (!Builder.GetInsertBlock())
5368 return InsertPointTy();
5369 // for by-ref case, the load is inside of the reduction region
5370 if (!IsByRef[En.index()])
5371 Builder.CreateStore(Reduced, RI.Variable);
5372 }
5373 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5374 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5375 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5376 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5377 Builder.CreateBr(ContinuationBlock);
5378
5379 // Populate the atomic reduction using the atomic elementwise reduction
5380 // function. There are no loads/stores here because they will be happening
5381 // inside the atomic elementwise reduction.
5382 Builder.SetInsertPoint(AtomicRedBlock);
5383 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5384 for (const ReductionInfo &RI : ReductionInfos) {
5386 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5387 if (!AfterIP)
5388 return AfterIP.takeError();
5389 Builder.restoreIP(*AfterIP);
5390 if (!Builder.GetInsertBlock())
5391 return InsertPointTy();
5392 }
5393 Builder.CreateBr(ContinuationBlock);
5394 } else {
5395 Builder.CreateUnreachable();
5396 }
5397
5398 // Populate the outlined reduction function using the elementwise reduction
5399 // function. Partial values are extracted from the type-erased array of
5400 // pointers to private variables.
5401 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5402 IsByRef, /*isGPU=*/false);
5403 if (Err)
5404 return Err;
5405
5406 if (!Builder.GetInsertBlock())
5407 return InsertPointTy();
5408
5409 Builder.SetInsertPoint(ContinuationBlock);
5410 return Builder.saveIP();
5411}
5412
5415 BodyGenCallbackTy BodyGenCB,
5416 FinalizeCallbackTy FiniCB) {
5417 if (!updateToLocation(Loc))
5418 return Loc.IP;
5419
5420 Directive OMPD = Directive::OMPD_master;
5421 uint32_t SrcLocStrSize;
5422 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5423 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5424 Value *ThreadId = getOrCreateThreadID(Ident);
5425 Value *Args[] = {Ident, ThreadId};
5426
5427 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5428 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5429
5430 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5431 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5432
5433 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5434 /*Conditional*/ true, /*hasFinalize*/ true);
5435}
5436
5439 BodyGenCallbackTy BodyGenCB,
5440 FinalizeCallbackTy FiniCB, Value *Filter) {
5442 if (!updateToLocation(Loc))
5443 return Loc.IP;
5444
5445 Directive OMPD = Directive::OMPD_masked;
5446 uint32_t SrcLocStrSize;
5447 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5448 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5449 Value *ThreadId = getOrCreateThreadID(Ident);
5450 Value *Args[] = {Ident, ThreadId, Filter};
5451 Value *ArgsEnd[] = {Ident, ThreadId};
5452
5453 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5454 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5455
5456 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5457 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5458
5459 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5460 /*Conditional*/ true, /*hasFinalize*/ true);
5461}
5462
5464 llvm::FunctionCallee Callee,
5466 const llvm::Twine &Name) {
5467 llvm::CallInst *Call = Builder.CreateCall(
5468 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5469 Call->setDoesNotThrow();
5470 return Call;
5471}
5472
5473// Expects input basic block is dominated by BeforeScanBB.
5474// Once Scan directive is encountered, the code after scan directive should be
5475// dominated by AfterScanBB. Scan directive splits the code sequence to
5476// scan and input phase. Based on whether inclusive or exclusive
5477// clause is used in the scan directive and whether input loop or scan loop
5478// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5479// input loop and second is the scan loop. The code generated handles only
5480// inclusive scans now.
5482 const LocationDescription &Loc, InsertPointTy AllocaIP,
5483 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5484 bool IsInclusive, ScanInfo *ScanRedInfo) {
5485 if (ScanRedInfo->OMPFirstScanLoop) {
5486 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5487 ScanVarsType, ScanRedInfo);
5488 if (Err)
5489 return Err;
5490 }
5491 if (!updateToLocation(Loc))
5492 return Loc.IP;
5493
5494 llvm::Value *IV = ScanRedInfo->IV;
5495
5496 if (ScanRedInfo->OMPFirstScanLoop) {
5497 // Emit buffer[i] = red; at the end of the input phase.
5498 for (size_t i = 0; i < ScanVars.size(); i++) {
5499 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5500 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5501 Type *DestTy = ScanVarsType[i];
5502 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5503 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5504
5505 Builder.CreateStore(Src, Val);
5506 }
5507 }
5508 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5509 emitBlock(ScanRedInfo->OMPScanDispatch,
5510 Builder.GetInsertBlock()->getParent());
5511
5512 if (!ScanRedInfo->OMPFirstScanLoop) {
5513 IV = ScanRedInfo->IV;
5514 // Emit red = buffer[i]; at the entrance to the scan phase.
5515 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5516 for (size_t i = 0; i < ScanVars.size(); i++) {
5517 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5518 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5519 Type *DestTy = ScanVarsType[i];
5520 Value *SrcPtr =
5521 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5522 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5523 Builder.CreateStore(Src, ScanVars[i]);
5524 }
5525 }
5526
5527 // TODO: Update it to CreateBr and remove dead blocks
5528 llvm::Value *CmpI = Builder.getInt1(true);
5529 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5530 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5531 ScanRedInfo->OMPAfterScanBlock);
5532 } else {
5533 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5534 ScanRedInfo->OMPBeforeScanBlock);
5535 }
5536 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5537 Builder.GetInsertBlock()->getParent());
5538 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5539 return Builder.saveIP();
5540}
5541
5542Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5543 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5544 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5545
5546 Builder.restoreIP(AllocaIP);
5547 // Create the shared pointer at alloca IP.
5548 for (size_t i = 0; i < ScanVars.size(); i++) {
5549 llvm::Value *BuffPtr =
5550 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5551 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5552 }
5553
5554 // Allocate temporary buffer by master thread
5555 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5556 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5557 Builder.restoreIP(CodeGenIP);
5558 Value *AllocSpan =
5559 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5560 for (size_t i = 0; i < ScanVars.size(); i++) {
5561 Type *IntPtrTy = Builder.getInt32Ty();
5562 Constant *Allocsize = ConstantExpr::getSizeOf(ScanVarsType[i]);
5563 Allocsize = ConstantExpr::getTruncOrBitCast(Allocsize, IntPtrTy);
5564 Value *Buff =
5565 Builder.CreateMalloc(IntPtrTy, Allocsize, AllocSpan, nullptr, "arr");
5566 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5567 }
5568 return Error::success();
5569 };
5570 // TODO: Perform finalization actions for variables. This has to be
5571 // called for variables which have destructors/finalizers.
5572 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5573
5574 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5575 llvm::Value *FilterVal = Builder.getInt32(0);
5577 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5578
5579 if (!AfterIP)
5580 return AfterIP.takeError();
5581 Builder.restoreIP(*AfterIP);
5582 BasicBlock *InputBB = Builder.GetInsertBlock();
5583 if (InputBB->hasTerminator())
5584 Builder.SetInsertPoint(InputBB->getTerminator());
5585 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5586 if (!AfterIP)
5587 return AfterIP.takeError();
5588 Builder.restoreIP(*AfterIP);
5589
5590 return Error::success();
5591}
5592
5593Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5594 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5595 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5596 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5597 Builder.restoreIP(CodeGenIP);
5598 for (ReductionInfo RedInfo : ReductionInfos) {
5599 Value *PrivateVar = RedInfo.PrivateVariable;
5600 Value *OrigVar = RedInfo.Variable;
5601 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5602 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5603
5604 Type *SrcTy = RedInfo.ElementType;
5605 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5606 "arrayOffset");
5607 Value *Src = Builder.CreateLoad(SrcTy, Val);
5608
5609 Builder.CreateStore(Src, OrigVar);
5610 Builder.CreateFree(Buff);
5611 }
5612 return Error::success();
5613 };
5614 // TODO: Perform finalization actions for variables. This has to be
5615 // called for variables which have destructors/finalizers.
5616 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5617
5618 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5619 Builder.SetInsertPoint(TI);
5620 else
5621 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5622
5623 llvm::Value *FilterVal = Builder.getInt32(0);
5625 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5626
5627 if (!AfterIP)
5628 return AfterIP.takeError();
5629 Builder.restoreIP(*AfterIP);
5630 BasicBlock *InputBB = Builder.GetInsertBlock();
5631 if (InputBB->hasTerminator())
5632 Builder.SetInsertPoint(InputBB->getTerminator());
5633 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5634 if (!AfterIP)
5635 return AfterIP.takeError();
5636 Builder.restoreIP(*AfterIP);
5637 return Error::success();
5638}
5639
5641 const LocationDescription &Loc,
5643 ScanInfo *ScanRedInfo) {
5644
5645 if (!updateToLocation(Loc))
5646 return Loc.IP;
5647 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5648 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5649 Builder.restoreIP(CodeGenIP);
5650 Function *CurFn = Builder.GetInsertBlock()->getParent();
5651 // for (int k = 0; k <= ceil(log2(n)); ++k)
5652 llvm::BasicBlock *LoopBB =
5653 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5654 llvm::BasicBlock *ExitBB =
5655 splitBB(Builder, false, "omp.outer.log.scan.exit");
5657 Builder.GetInsertBlock()->getModule(),
5658 (llvm::Intrinsic::ID)llvm::Intrinsic::log2, Builder.getDoubleTy());
5659 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5660 llvm::Value *Arg =
5661 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5662 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5664 Builder.GetInsertBlock()->getModule(),
5665 (llvm::Intrinsic::ID)llvm::Intrinsic::ceil, Builder.getDoubleTy());
5666 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5667 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5668 llvm::Value *NMin1 = Builder.CreateNUWSub(
5669 ScanRedInfo->Span,
5670 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5671 Builder.SetInsertPoint(InputBB);
5672 Builder.CreateBr(LoopBB);
5673 emitBlock(LoopBB, CurFn);
5674 Builder.SetInsertPoint(LoopBB);
5675
5676 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5677 // size pow2k = 1;
5678 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5679 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5680 InputBB);
5681 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5682 InputBB);
5683 // for (size i = n - 1; i >= 2 ^ k; --i)
5684 // tmp[i] op= tmp[i-pow2k];
5685 llvm::BasicBlock *InnerLoopBB =
5686 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5687 llvm::BasicBlock *InnerExitBB =
5688 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5689 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5690 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5691 emitBlock(InnerLoopBB, CurFn);
5692 Builder.SetInsertPoint(InnerLoopBB);
5693 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5694 IVal->addIncoming(NMin1, LoopBB);
5695 for (ReductionInfo RedInfo : ReductionInfos) {
5696 Value *ReductionVal = RedInfo.PrivateVariable;
5697 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5698 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5699 Type *DestTy = RedInfo.ElementType;
5700 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5701 Value *LHSPtr =
5702 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5703 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5704 Value *RHSPtr =
5705 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5706 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5707 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5708 llvm::Value *Result;
5709 InsertPointOrErrorTy AfterIP =
5710 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5711 if (!AfterIP)
5712 return AfterIP.takeError();
5713 Builder.CreateStore(Result, LHSPtr);
5714 }
5715 llvm::Value *NextIVal = Builder.CreateNUWSub(
5716 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5717 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5718 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5719 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5720 emitBlock(InnerExitBB, CurFn);
5721 llvm::Value *Next = Builder.CreateNUWAdd(
5722 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5723 Counter->addIncoming(Next, Builder.GetInsertBlock());
5724 // pow2k <<= 1;
5725 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5726 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5727 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5728 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5729 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5730 return Error::success();
5731 };
5732
5733 // TODO: Perform finalization actions for variables. This has to be
5734 // called for variables which have destructors/finalizers.
5735 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5736
5737 llvm::Value *FilterVal = Builder.getInt32(0);
5739 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5740
5741 if (!AfterIP)
5742 return AfterIP.takeError();
5743 Builder.restoreIP(*AfterIP);
5744 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5745
5746 if (!AfterIP)
5747 return AfterIP.takeError();
5748 Builder.restoreIP(*AfterIP);
5749 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5750 if (Err)
5751 return Err;
5752
5753 return AfterIP;
5754}
5755
5756Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5757 llvm::function_ref<Error()> InputLoopGen,
5758 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5759 ScanInfo *ScanRedInfo) {
5760
5761 {
5762 // Emit loop with input phase:
5763 // for (i: 0..<num_iters>) {
5764 // <input phase>;
5765 // buffer[i] = red;
5766 // }
5767 ScanRedInfo->OMPFirstScanLoop = true;
5768 Error Err = InputLoopGen();
5769 if (Err)
5770 return Err;
5771 }
5772 {
5773 // Emit loop with scan phase:
5774 // for (i: 0..<num_iters>) {
5775 // red = buffer[i];
5776 // <scan phase>;
5777 // }
5778 ScanRedInfo->OMPFirstScanLoop = false;
5779 Error Err = ScanLoopGen(Builder);
5780 if (Err)
5781 return Err;
5782 }
5783 return Error::success();
5784}
5785
5786void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5787 Function *Fun = Builder.GetInsertBlock()->getParent();
5788 ScanRedInfo->OMPScanDispatch =
5789 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5790 ScanRedInfo->OMPAfterScanBlock =
5791 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5792 ScanRedInfo->OMPBeforeScanBlock =
5793 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5794 ScanRedInfo->OMPScanLoopExit =
5795 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5796}
5798 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5799 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5800 Module *M = F->getParent();
5801 LLVMContext &Ctx = M->getContext();
5802 Type *IndVarTy = TripCount->getType();
5803
5804 // Create the basic block structure.
5805 BasicBlock *Preheader =
5806 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5807 BasicBlock *Header =
5808 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5809 BasicBlock *Cond =
5810 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5811 BasicBlock *Body =
5812 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5813 BasicBlock *Latch =
5814 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5815 BasicBlock *Exit =
5816 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5817 BasicBlock *After =
5818 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5819
5820 // Use specified DebugLoc for new instructions.
5821 Builder.SetCurrentDebugLocation(DL);
5822
5823 Builder.SetInsertPoint(Preheader);
5824 Builder.CreateBr(Header);
5825
5826 Builder.SetInsertPoint(Header);
5827 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5828 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5829 Builder.CreateBr(Cond);
5830
5831 Builder.SetInsertPoint(Cond);
5832 Value *Cmp =
5833 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5834 Builder.CreateCondBr(Cmp, Body, Exit);
5835
5836 Builder.SetInsertPoint(Body);
5837 Builder.CreateBr(Latch);
5838
5839 Builder.SetInsertPoint(Latch);
5840 // Decide whether the induction variable increment can carry nsw.
5841 //
5842 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5843 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5844 // for valid programs 0 <= count <= INT_MAX always holds.
5845 //
5846 // Collapsed loops: the trip count is a product that can overflow i32 even for
5847 // a conforming program, so nsw is kept only when the product is a constant
5848 // that provably fits, dropped otherwise.
5849 bool HasNSW = Config.hasNoSignedWrap();
5850 if (HasNSW) {
5851 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5852 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5854 if (CI->getValue().ugt(SignedMax))
5855 HasNSW = false;
5856 } else if (IsCollapsed) {
5857 HasNSW = false;
5858 }
5859 }
5860 Value *Next =
5861 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5862 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5863 Builder.CreateBr(Header);
5864 IndVarPHI->addIncoming(Next, Latch);
5865
5866 Builder.SetInsertPoint(Exit);
5867 Builder.CreateBr(After);
5868
5869 // Remember and return the canonical control flow.
5870 LoopInfos.emplace_front();
5871 CanonicalLoopInfo *CL = &LoopInfos.front();
5872
5873 CL->Header = Header;
5874 CL->Cond = Cond;
5875 CL->Latch = Latch;
5876 CL->Exit = Exit;
5877
5878#ifndef NDEBUG
5879 CL->assertOK();
5880#endif
5881 return CL;
5882}
5883
5886 LoopBodyGenCallbackTy BodyGenCB,
5887 Value *TripCount, const Twine &Name) {
5888 BasicBlock *BB = Loc.IP.getBlock();
5889 BasicBlock *NextBB = BB->getNextNode();
5890
5891 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5892 NextBB, NextBB, Name);
5893 BasicBlock *After = CL->getAfter();
5894
5895 // If location is not set, don't connect the loop.
5896 if (updateToLocation(Loc)) {
5897 // Split the loop at the insertion point: Branch to the preheader and move
5898 // every following instruction to after the loop (the After BB). Also, the
5899 // new successor is the loop's after block.
5900 spliceBB(Builder, After, /*CreateBranch=*/false);
5901 Builder.CreateBr(CL->getPreheader());
5902 }
5903
5904 // Emit the body content. We do it after connecting the loop to the CFG to
5905 // avoid that the callback encounters degenerate BBs.
5906 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5907 return Err;
5908
5909#ifndef NDEBUG
5910 CL->assertOK();
5911#endif
5912 return CL;
5913}
5914
5916 ScanInfos.emplace_front();
5917 ScanInfo *Result = &ScanInfos.front();
5918 return Result;
5919}
5920
5924 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5925 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5926 LocationDescription ComputeLoc =
5927 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5928 updateToLocation(ComputeLoc);
5929
5931
5933 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5934 ScanRedInfo->Span = TripCount;
5935 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5936 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5937
5938 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5939 Builder.restoreIP(CodeGenIP);
5940 ScanRedInfo->IV = IV;
5941 createScanBBs(ScanRedInfo);
5942 BasicBlock *InputBlock = Builder.GetInsertBlock();
5943 Instruction *Terminator = InputBlock->getTerminator();
5944 assert(Terminator->getNumSuccessors() == 1);
5945 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5946 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5947 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5948 Builder.GetInsertBlock()->getParent());
5949 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5950 emitBlock(ScanRedInfo->OMPScanLoopExit,
5951 Builder.GetInsertBlock()->getParent());
5952 Builder.CreateBr(ContinueBlock);
5953 Builder.SetInsertPoint(
5954 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5955 return BodyGenCB(Builder.saveIP(), IV);
5956 };
5957
5958 const auto &&InputLoopGen = [&]() -> Error {
5960 createCanonicalLoop(Builder, BodyGen, Start, Stop, Step, IsSigned,
5961 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5962 if (!LoopInfo)
5963 return LoopInfo.takeError();
5964 Result.push_back(*LoopInfo);
5965 Builder.restoreIP((*LoopInfo)->getAfterIP());
5966 return Error::success();
5967 };
5968 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5970 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5971 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5972 if (!LoopInfo)
5973 return LoopInfo.takeError();
5974 Result.push_back(*LoopInfo);
5975 Builder.restoreIP((*LoopInfo)->getAfterIP());
5976 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5977 return Error::success();
5978 };
5979 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5980 if (Err)
5981 return Err;
5982 return Result;
5983}
5984
5986 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5987 bool IsSigned, bool InclusiveStop, const Twine &Name) {
5988
5989 // Consider the following difficulties (assuming 8-bit signed integers):
5990 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
5991 // DO I = 1, 100, 50
5992 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
5993 // DO I = 100, 0, -128
5994
5995 // Start, Stop and Step must be of the same integer type.
5996 auto *IndVarTy = cast<IntegerType>(Start->getType());
5997 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
5998 assert(IndVarTy == Step->getType() && "Step type mismatch");
5999
6001
6002 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
6003 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
6004
6005 // Like Step, but always positive.
6006 Value *Incr = Step;
6007
6008 // Distance between Start and Stop; always positive.
6009 Value *Span;
6010
6011 // Condition whether there are no iterations are executed at all, e.g. because
6012 // UB < LB.
6013 Value *ZeroCmp;
6014
6015 if (IsSigned) {
6016 // Ensure that increment is positive. If not, negate and invert LB and UB.
6017 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
6018 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
6019 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6020 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6021 Span = Builder.CreateSub(UB, LB, "", false, true);
6022 ZeroCmp = Builder.CreateICmp(
6023 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6024 } else {
6025 Span = Builder.CreateSub(Stop, Start, "", true);
6026 ZeroCmp = Builder.CreateICmp(
6027 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6028 }
6029
6030 Value *CountIfLooping;
6031 if (InclusiveStop) {
6032 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6033 } else {
6034 // Avoid incrementing past stop since it could overflow.
6035 Value *CountIfTwo = Builder.CreateAdd(
6036 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6037 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6038 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6039 }
6040
6041 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6042 "omp_" + Name + ".tripcount");
6043}
6044
6047 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6048 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6049 ScanInfo *ScanRedInfo) {
6050 LocationDescription ComputeLoc =
6051 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6052
6054 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6055
6056 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6057 Builder.restoreIP(CodeGenIP);
6058 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6059 /*HasNSW=*/Config.hasNoSignedWrap());
6060 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6061 /*HasNSW=*/Config.hasNoSignedWrap());
6062 if (InScan)
6063 ScanRedInfo->IV = IndVar;
6064 return BodyGenCB(Builder.saveIP(), IndVar);
6065 };
6066 LocationDescription LoopLoc =
6067 ComputeIP.isSet()
6068 ? Loc
6069 : LocationDescription(Builder.saveIP(),
6070 Builder.getCurrentDebugLocation());
6071 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6072}
6073
6074// Returns an LLVM function to call for initializing loop bounds using OpenMP
6075// static scheduling for composite `distribute parallel for` depending on
6076// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6077// integers as unsigned similarly to CanonicalLoopInfo.
6078static FunctionCallee
6080 OpenMPIRBuilder &OMPBuilder) {
6081 unsigned Bitwidth = Ty->getIntegerBitWidth();
6082 if (Bitwidth == 32)
6083 return OMPBuilder.getOrCreateRuntimeFunction(
6084 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6085 if (Bitwidth == 64)
6086 return OMPBuilder.getOrCreateRuntimeFunction(
6087 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6088 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6089}
6090
6091// Returns an LLVM function to call for initializing loop bounds using OpenMP
6092// static scheduling depending on `type`. Only i32 and i64 are supported by the
6093// runtime. Always interpret integers as unsigned similarly to
6094// CanonicalLoopInfo.
6096 OpenMPIRBuilder &OMPBuilder) {
6097 unsigned Bitwidth = Ty->getIntegerBitWidth();
6098 if (Bitwidth == 32)
6099 return OMPBuilder.getOrCreateRuntimeFunction(
6100 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6101 if (Bitwidth == 64)
6102 return OMPBuilder.getOrCreateRuntimeFunction(
6103 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6104 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6105}
6106
6107OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6108 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6109 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6110 OMPScheduleType DistScheduleSchedType) {
6111 assert(CLI->isValid() && "Requires a valid canonical loop");
6112 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6113 "Require dedicated allocate IP");
6114
6115 // Set up the source location value for OpenMP runtime.
6116 Builder.restoreIP(CLI->getPreheaderIP());
6117 Builder.SetCurrentDebugLocation(DL);
6118
6119 uint32_t SrcLocStrSize;
6120 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6122 switch (LoopType) {
6123 case WorksharingLoopType::ForStaticLoop:
6124 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6125 break;
6126 case WorksharingLoopType::DistributeStaticLoop:
6127 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6128 break;
6129 case WorksharingLoopType::DistributeForStaticLoop:
6130 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6131 break;
6132 }
6133 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6134
6135 // Declare useful OpenMP runtime functions.
6136 Value *IV = CLI->getIndVar();
6137 Type *IVTy = IV->getType();
6138 FunctionCallee StaticInit =
6139 LoopType == WorksharingLoopType::DistributeForStaticLoop
6140 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6141 : getKmpcForStaticInitForType(IVTy, M, *this);
6142 FunctionCallee StaticFini =
6143 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6144
6145 // Allocate space for computed loop bounds as expected by the "init" function.
6146 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6147
6148 Type *I32Type = Type::getInt32Ty(M.getContext());
6149 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6150 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6151 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6152 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6153 CLI->setLastIter(PLastIter);
6154
6155 // At the end of the preheader, prepare for calling the "init" function by
6156 // storing the current loop bounds into the allocated space. A canonical loop
6157 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6158 // and produces an inclusive upper bound.
6159 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6160 Constant *Zero = ConstantInt::get(IVTy, 0);
6161 Constant *One = ConstantInt::get(IVTy, 1);
6162 Builder.CreateStore(Zero, PLowerBound);
6163 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6164 Builder.CreateStore(UpperBound, PUpperBound);
6165 Builder.CreateStore(One, PStride);
6166
6167 Value *ThreadNum =
6168 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6169
6170 OMPScheduleType SchedType =
6171 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6172 ? OMPScheduleType::OrderedDistribute
6174 Constant *SchedulingType =
6175 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6176
6177 // Call the "init" function and update the trip count of the loop with the
6178 // value it produced.
6179 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6180 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6181 this](Value *SchedulingType, auto &Builder) {
6182 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6183 PLowerBound, PUpperBound});
6184 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6185 Value *PDistUpperBound =
6186 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6187 Args.push_back(PDistUpperBound);
6188 }
6189 Args.append({PStride, One, Zero});
6190 createRuntimeFunctionCall(StaticInit, Args);
6191 };
6192 BuildInitCall(SchedulingType, Builder);
6193 if (HasDistSchedule &&
6194 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6195 Constant *DistScheduleSchedType = ConstantInt::get(
6196 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6197 // We want to emit a second init function call for the dist_schedule clause
6198 // to the Distribute construct. This should only be done however if a
6199 // Workshare Loop is nested within a Distribute Construct
6200 BuildInitCall(DistScheduleSchedType, Builder);
6201 }
6202 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6203 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6204 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6205 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6206 CLI->setTripCount(TripCount);
6207
6208 // Update all uses of the induction variable except the one in the condition
6209 // block that compares it with the actual upper bound, and the increment in
6210 // the latch block.
6211
6212 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6213 Builder.SetInsertPoint(CLI->getBody(),
6214 CLI->getBody()->getFirstInsertionPt());
6215 Builder.SetCurrentDebugLocation(DL);
6216 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6217 /*HasNSW=*/Config.hasNoSignedWrap());
6218 });
6219
6220 // In the "exit" block, call the "fini" function.
6221 Builder.SetInsertPoint(CLI->getExit(),
6222 CLI->getExit()->getTerminator()->getIterator());
6223 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6224
6225 // Add the barrier if requested.
6226 if (NeedsBarrier) {
6227 InsertPointOrErrorTy BarrierIP =
6229 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6230 /* CheckCancelFlag */ false);
6231 if (!BarrierIP)
6232 return BarrierIP.takeError();
6233 }
6234
6235 InsertPointTy AfterIP = CLI->getAfterIP();
6236 CLI->invalidate();
6237
6238 return AfterIP;
6239}
6240
6241static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6242 LoopInfo &LI);
6243static void addLoopMetadata(CanonicalLoopInfo *Loop,
6245
6247 LLVMContext &Ctx, Loop *Loop,
6249 SmallVector<Metadata *> &LoopMDList) {
6250 SmallSet<BasicBlock *, 8> Reachable;
6251
6252 // Get the basic blocks from the loop in which memref instructions
6253 // can be found.
6254 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6255 // preferably without running any passes.
6256 for (BasicBlock *Block : Loop->getBlocks()) {
6257 if (Block == CLI->getCond() || Block == CLI->getHeader())
6258 continue;
6259 Reachable.insert(Block);
6260 }
6261
6262 // Add access group metadata to memory-access instructions.
6264 for (BasicBlock *BB : Reachable)
6266 // TODO: If the loop has existing parallel access metadata, have
6267 // to combine two lists.
6268 LoopMDList.push_back(MDNode::get(
6269 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6270}
6271
6273OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6274 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6275 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6276 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6277 assert(CLI->isValid() && "Requires a valid canonical loop");
6278 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6279
6280 LLVMContext &Ctx = CLI->getFunction()->getContext();
6281 Value *IV = CLI->getIndVar();
6282 Value *OrigTripCount = CLI->getTripCount();
6283 Type *IVTy = IV->getType();
6284 assert(IVTy->getIntegerBitWidth() <= 64 &&
6285 "Max supported tripcount bitwidth is 64 bits");
6286 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6287 : Type::getInt64Ty(Ctx);
6288 Type *I32Type = Type::getInt32Ty(M.getContext());
6289 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6290 Constant *One = ConstantInt::get(InternalIVTy, 1);
6291
6292 Function *F = CLI->getFunction();
6293 // Blocks must have terminators.
6294 // FIXME: Don't run analyses on incomplete/invalid IR.
6295 SmallVector<Instruction *> UIs;
6296 for (BasicBlock &BB : *F)
6297 if (!BB.hasTerminator())
6298 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6300 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6301 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6302 LoopAnalysis LIA;
6303 LoopInfo &&LI = LIA.run(*F, FAM);
6304 for (Instruction *I : UIs)
6305 I->eraseFromParent();
6306 Loop *L = LI.getLoopFor(CLI->getHeader());
6307 SmallVector<Metadata *> LoopMDList;
6308 if (ChunkSize || DistScheduleChunkSize)
6309 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6310 addLoopMetadata(CLI, LoopMDList);
6311
6312 // Declare useful OpenMP runtime functions.
6313 FunctionCallee StaticInit =
6314 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6315 FunctionCallee StaticFini =
6316 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6317
6318 // Allocate space for computed loop bounds as expected by the "init" function.
6319 Builder.restoreIP(AllocaIP);
6320 Builder.SetCurrentDebugLocation(DL);
6321 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6322 Value *PLowerBound =
6323 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6324 Value *PUpperBound =
6325 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6326 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6327 CLI->setLastIter(PLastIter);
6328
6329 // Set up the source location value for the OpenMP runtime.
6330 Builder.restoreIP(CLI->getPreheaderIP());
6331 Builder.SetCurrentDebugLocation(DL);
6332
6333 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6334 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6335 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6336 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6337 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6338 "distschedulechunksize");
6339 Value *CastedTripCount =
6340 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6341
6342 Constant *SchedulingType =
6343 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6344 Constant *DistSchedulingType =
6345 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6346 Builder.CreateStore(Zero, PLowerBound);
6347 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6348 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6349 Value *UpperBound =
6350 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6351 Builder.CreateStore(UpperBound, PUpperBound);
6352 Builder.CreateStore(One, PStride);
6353
6354 // Call the "init" function and update the trip count of the loop with the
6355 // value it produced.
6356 uint32_t SrcLocStrSize;
6357 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6358 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6359 if (DistScheduleSchedType != OMPScheduleType::None) {
6360 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6361 }
6362 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6363 Value *ThreadNum =
6364 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6365 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6366 PUpperBound, PStride, One,
6367 this](Value *SchedulingType, Value *ChunkSize,
6368 auto &Builder) {
6370 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6371 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6372 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6373 /*pstride=*/PStride, /*incr=*/One,
6374 /*chunk=*/ChunkSize});
6375 };
6376 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6377 if (DistScheduleSchedType != OMPScheduleType::None &&
6378 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6379 SchedType != OMPScheduleType::OrderedDistribute) {
6380 // We want to emit a second init function call for the dist_schedule clause
6381 // to the Distribute construct. This should only be done however if a
6382 // Workshare Loop is nested within a Distribute Construct
6383 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6384 }
6385
6386 // Load values written by the "init" function.
6387 Value *FirstChunkStart =
6388 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6389 Value *FirstChunkStop =
6390 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6391 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6392 Value *ChunkRange =
6393 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6394 Value *NextChunkStride =
6395 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6396
6397 // Create outer "dispatch" loop for enumerating the chunks.
6398 BasicBlock *DispatchEnter = splitBB(Builder, true);
6399 Value *DispatchCounter;
6400
6401 // It is safe to assume this didn't return an error because the callback
6402 // passed into createCanonicalLoop is the only possible error source, and it
6403 // always returns success.
6404 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6405 {Builder.saveIP(), DL},
6406 [&](InsertPointTy BodyIP, Value *Counter) {
6407 DispatchCounter = Counter;
6408 return Error::success();
6409 },
6410 FirstChunkStart, CastedTripCount, NextChunkStride,
6411 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6412 "dispatch"));
6413
6414 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6415 // not have to preserve the canonical invariant.
6416 BasicBlock *DispatchBody = DispatchCLI->getBody();
6417 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6418 BasicBlock *DispatchExit = DispatchCLI->getExit();
6419 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6420 DispatchCLI->invalidate();
6421
6422 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6423 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6424 redirectTo(CLI->getExit(), DispatchLatch, DL);
6425 redirectTo(DispatchBody, DispatchEnter, DL);
6426
6427 // Prepare the prolog of the chunk loop.
6428 Builder.restoreIP(CLI->getPreheaderIP());
6429 Builder.SetCurrentDebugLocation(DL);
6430
6431 // Compute the number of iterations of the chunk loop.
6432 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6433 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6434 Value *IsLastChunk =
6435 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6436 Value *CountUntilOrigTripCount =
6437 Builder.CreateSub(CastedTripCount, DispatchCounter);
6438 Value *ChunkTripCount = Builder.CreateSelect(
6439 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6440 Value *BackcastedChunkTC =
6441 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6442 CLI->setTripCount(BackcastedChunkTC);
6443
6444 // Update all uses of the induction variable except the one in the condition
6445 // block that compares it with the actual upper bound, and the increment in
6446 // the latch block.
6447 Value *BackcastedDispatchCounter =
6448 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6449 CLI->mapIndVar([&](Instruction *) -> Value * {
6450 Builder.restoreIP(CLI->getBodyIP());
6451 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6452 });
6453
6454 // In the "exit" block, call the "fini" function.
6455 Builder.SetInsertPoint(DispatchExit, DispatchExit->getFirstInsertionPt());
6456 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6457
6458 // Add the barrier if requested.
6459 if (NeedsBarrier) {
6460 InsertPointOrErrorTy AfterIP =
6461 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6462 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6463 if (!AfterIP)
6464 return AfterIP.takeError();
6465 }
6466
6467#ifndef NDEBUG
6468 // Even though we currently do not support applying additional methods to it,
6469 // the chunk loop should remain a canonical loop.
6470 CLI->assertOK();
6471#endif
6472
6473 return InsertPointTy(DispatchAfter, DispatchAfter->getFirstInsertionPt());
6474}
6475
6476// Returns an LLVM function to call for executing an OpenMP static worksharing
6477// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6478// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6479static FunctionCallee
6481 WorksharingLoopType LoopType) {
6482 unsigned Bitwidth = Ty->getIntegerBitWidth();
6483 Module &M = OMPBuilder->M;
6484 switch (LoopType) {
6485 case WorksharingLoopType::ForStaticLoop:
6486 if (Bitwidth == 32)
6487 return OMPBuilder->getOrCreateRuntimeFunction(
6488 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6489 if (Bitwidth == 64)
6490 return OMPBuilder->getOrCreateRuntimeFunction(
6491 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6492 break;
6493 case WorksharingLoopType::DistributeStaticLoop:
6494 if (Bitwidth == 32)
6495 return OMPBuilder->getOrCreateRuntimeFunction(
6496 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6497 if (Bitwidth == 64)
6498 return OMPBuilder->getOrCreateRuntimeFunction(
6499 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6500 break;
6501 case WorksharingLoopType::DistributeForStaticLoop:
6502 if (Bitwidth == 32)
6503 return OMPBuilder->getOrCreateRuntimeFunction(
6504 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6505 if (Bitwidth == 64)
6506 return OMPBuilder->getOrCreateRuntimeFunction(
6507 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6508 break;
6509 }
6510 if (Bitwidth != 32 && Bitwidth != 64) {
6511 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6512 }
6513 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6514}
6515
6516// Inserts a call to proper OpenMP Device RTL function which handles
6517// loop worksharing.
6519 WorksharingLoopType LoopType,
6520 BasicBlock *InsertBlock, Value *Ident,
6521 Value *LoopBodyArg, Value *TripCount,
6522 Function &LoopBodyFn, bool NoLoop) {
6523 Type *TripCountTy = TripCount->getType();
6524 Module &M = OMPBuilder->M;
6525 IRBuilder<> &Builder = OMPBuilder->Builder;
6526 FunctionCallee RTLFn =
6527 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6528 SmallVector<Value *, 8> RealArgs;
6529 RealArgs.push_back(Ident);
6530 RealArgs.push_back(&LoopBodyFn);
6531 RealArgs.push_back(LoopBodyArg);
6532 RealArgs.push_back(TripCount);
6533 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6534 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6535 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6536 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6537 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6538 return;
6539 }
6540 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6541 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6542 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6543 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6544
6545 RealArgs.push_back(
6546 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6547 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6548 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6549 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6550 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6551 } else {
6552 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6553 }
6554
6555 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6556}
6557
6559 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6560 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6561 WorksharingLoopType LoopType, bool NoLoop) {
6562 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6563 BasicBlock *Preheader = CLI->getPreheader();
6564 Value *TripCount = CLI->getTripCount();
6565
6566 // After loop body outling, the loop body contains only set up
6567 // of loop body argument structure and the call to the outlined
6568 // loop body function. Firstly, we need to move setup of loop body args
6569 // into loop preheader.
6570 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6571 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6572
6573 // The next step is to remove the whole loop. We do not it need anymore.
6574 // That's why make an unconditional branch from loop preheader to loop
6575 // exit block
6576 Builder.restoreIP({Preheader, Preheader->end()});
6577 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6578 Preheader->getTerminator()->eraseFromParent();
6579 Builder.CreateBr(CLI->getExit());
6580
6581 // Delete dead loop blocks
6582 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6583 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6584 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6585 CleanUpInfo.EntryBB = CLI->getHeader();
6586 CleanUpInfo.ExitBB = CLI->getExit();
6587 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6588 DeleteDeadBlocks(BlocksToBeRemoved);
6589
6590 // Find the instruction which corresponds to loop body argument structure
6591 // and remove the call to loop body function instruction.
6592 Value *LoopBodyArg;
6593 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6594 assert(OutlinedFnUser &&
6595 "Expected unique undroppable user of outlined function");
6596 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6597 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6598 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6599 "Expected outlined function call to be located in loop preheader");
6600 // Check in case no argument structure has been passed.
6601 if (OutlinedFnCallInstruction->arg_size() > 1)
6602 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6603 else
6604 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6605 OutlinedFnCallInstruction->eraseFromParent();
6606
6607 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6608 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6609
6610 for (auto &ToBeDeletedItem : ToBeDeleted)
6611 ToBeDeletedItem->eraseFromParent();
6612 CLI->invalidate();
6613}
6614
6615OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6616 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6617 WorksharingLoopType LoopType, bool NoLoop) {
6618 uint32_t SrcLocStrSize;
6619 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6621 switch (LoopType) {
6622 case WorksharingLoopType::ForStaticLoop:
6623 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6624 break;
6625 case WorksharingLoopType::DistributeStaticLoop:
6626 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6627 break;
6628 case WorksharingLoopType::DistributeForStaticLoop:
6629 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6630 break;
6631 }
6632 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6633
6634 auto OI = std::make_unique<OutlineInfo>();
6635 OI->OuterAllocBB = CLI->getPreheader();
6636 Function *OuterFn = CLI->getPreheader()->getParent();
6637
6638 // Instructions which need to be deleted at the end of code generation
6639 SmallVector<Instruction *, 4> ToBeDeleted;
6640
6641 OI->OuterAllocBB = AllocaIP.getBlock();
6642
6643 // Mark the body loop as region which needs to be extracted
6644 OI->EntryBB = CLI->getBody();
6645 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6646 "omp.prelatch");
6647
6648 // Prepare loop body for extraction
6649 Builder.restoreIP({CLI->getPreheader(), CLI->getPreheader()->begin()});
6650
6651 // Insert new loop counter variable which will be used only in loop
6652 // body.
6653 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6654 Instruction *NewLoopCntLoad =
6655 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6656 // New loop counter instructions are redundant in the loop preheader when
6657 // code generation for workshare loop is finshed. That's why mark them as
6658 // ready for deletion.
6659 ToBeDeleted.push_back(NewLoopCntLoad);
6660 ToBeDeleted.push_back(NewLoopCnt);
6661
6662 // Analyse loop body region. Find all input variables which are used inside
6663 // loop body region.
6664 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6666 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6667
6668 CodeExtractorAnalysisCache CEAC(*OuterFn);
6669 CodeExtractor Extractor(Blocks,
6670 /* DominatorTree */ nullptr,
6671 /* AggregateArgs */ true,
6672 /* BlockFrequencyInfo */ nullptr,
6673 /* BranchProbabilityInfo */ nullptr,
6674 /* AssumptionCache */ nullptr,
6675 /* AllowVarArgs */ true,
6676 /* AllowAlloca */ true,
6677 /* AllocationBlock */ CLI->getPreheader(),
6678 /* DeallocationBlocks */ {},
6679 /* Suffix */ ".omp_wsloop",
6680 /* AggrArgsIn0AddrSpace */ true);
6681
6682 BasicBlock *CommonExit = nullptr;
6683 SetVector<Value *> SinkingCands, HoistingCands;
6684
6685 // Find allocas outside the loop body region which are used inside loop
6686 // body
6687 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6688
6689 // We need to model loop body region as the function f(cnt, loop_arg).
6690 // That's why we replace loop induction variable by the new counter
6691 // which will be one of loop body function argument
6693 CLI->getIndVar()->user_end());
6694 for (auto Use : Users) {
6695 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6696 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6697 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6698 }
6699 }
6700 }
6701 // Make sure that loop counter variable is not merged into loop body
6702 // function argument structure and it is passed as separate variable
6703 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6704
6705 // PostOutline CB is invoked when loop body function is outlined and
6706 // loop body is replaced by call to outlined function. We need to add
6707 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6708 // function will handle loop control logic.
6709 //
6710 OI->PostOutlineCB = [=, ToBeDeletedVec =
6711 std::move(ToBeDeleted)](Function &OutlinedFn) {
6712 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6713 LoopType, NoLoop);
6714 };
6715 addOutlineInfo(std::move(OI));
6716 return CLI->getAfterIP();
6717}
6718
6721 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6722 bool HasSimdModifier, bool HasMonotonicModifier,
6723 bool HasNonmonotonicModifier, bool HasOrderedClause,
6724 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6725 Value *DistScheduleChunkSize) {
6726 if (Config.isTargetDevice())
6727 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NoLoop);
6728 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6729 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6730 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6731
6732 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6733 OMPScheduleType::ModifierOrdered;
6734 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6735 if (HasDistSchedule) {
6736 DistScheduleSchedType = DistScheduleChunkSize
6737 ? OMPScheduleType::OrderedDistributeChunked
6738 : OMPScheduleType::OrderedDistribute;
6739 }
6740 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6741 case OMPScheduleType::BaseStatic:
6742 case OMPScheduleType::BaseDistribute:
6743 assert((!ChunkSize || !DistScheduleChunkSize) &&
6744 "No chunk size with static-chunked schedule");
6745 if (IsOrdered && !HasDistSchedule)
6746 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6747 NeedsBarrier, ChunkSize);
6748 // FIXME: Monotonicity ignored?
6749 if (DistScheduleChunkSize)
6750 return applyStaticChunkedWorkshareLoop(
6751 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6752 DistScheduleChunkSize, DistScheduleSchedType);
6753 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6754 HasDistSchedule);
6755
6756 case OMPScheduleType::BaseStaticChunked:
6757 case OMPScheduleType::BaseDistributeChunked:
6758 if (IsOrdered && !HasDistSchedule)
6759 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6760 NeedsBarrier, ChunkSize);
6761 // FIXME: Monotonicity ignored?
6762 return applyStaticChunkedWorkshareLoop(
6763 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6764 DistScheduleChunkSize, DistScheduleSchedType);
6765
6766 case OMPScheduleType::BaseRuntime:
6767 case OMPScheduleType::BaseAuto:
6768 case OMPScheduleType::BaseGreedy:
6769 case OMPScheduleType::BaseBalanced:
6770 case OMPScheduleType::BaseSteal:
6771 case OMPScheduleType::BaseRuntimeSimd:
6772 assert(!ChunkSize &&
6773 "schedule type does not support user-defined chunk sizes");
6774 [[fallthrough]];
6775 case OMPScheduleType::BaseGuidedSimd:
6776 case OMPScheduleType::BaseDynamicChunked:
6777 case OMPScheduleType::BaseGuidedChunked:
6778 case OMPScheduleType::BaseGuidedIterativeChunked:
6779 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6780 case OMPScheduleType::BaseStaticBalancedChunked:
6781 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6782 NeedsBarrier, ChunkSize);
6783
6784 default:
6785 llvm_unreachable("Unknown/unimplemented schedule kind");
6786 }
6787}
6788
6789/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6790/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6791/// the runtime. Always interpret integers as unsigned similarly to
6792/// CanonicalLoopInfo.
6793static FunctionCallee
6795 unsigned Bitwidth = Ty->getIntegerBitWidth();
6796 if (Bitwidth == 32)
6797 return OMPBuilder.getOrCreateRuntimeFunction(
6798 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6799 if (Bitwidth == 64)
6800 return OMPBuilder.getOrCreateRuntimeFunction(
6801 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6802 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6803}
6804
6805/// Returns an LLVM function to call for updating the next loop using OpenMP
6806/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6807/// the runtime. Always interpret integers as unsigned similarly to
6808/// CanonicalLoopInfo.
6809static FunctionCallee
6811 unsigned Bitwidth = Ty->getIntegerBitWidth();
6812 if (Bitwidth == 32)
6813 return OMPBuilder.getOrCreateRuntimeFunction(
6814 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6815 if (Bitwidth == 64)
6816 return OMPBuilder.getOrCreateRuntimeFunction(
6817 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6818 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6819}
6820
6821/// Returns an LLVM function to call for finalizing the dynamic loop using
6822/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6823/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6824static FunctionCallee
6826 unsigned Bitwidth = Ty->getIntegerBitWidth();
6827 if (Bitwidth == 32)
6828 return OMPBuilder.getOrCreateRuntimeFunction(
6829 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6830 if (Bitwidth == 64)
6831 return OMPBuilder.getOrCreateRuntimeFunction(
6832 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6833 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6834}
6835
6837OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6838 InsertPointTy AllocaIP,
6839 OMPScheduleType SchedType,
6840 bool NeedsBarrier, Value *Chunk) {
6841 assert(CLI->isValid() && "Requires a valid canonical loop");
6842 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6843 "Require dedicated allocate IP");
6845 "Require valid schedule type");
6846
6847 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6848 OMPScheduleType::ModifierOrdered;
6849
6850 // Set up the source location value for OpenMP runtime.
6851 Builder.SetCurrentDebugLocation(DL);
6852
6853 uint32_t SrcLocStrSize;
6854 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6855 Value *SrcLoc =
6856 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6857
6858 // Declare useful OpenMP runtime functions.
6859 Value *IV = CLI->getIndVar();
6860 Type *IVTy = IV->getType();
6861 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6862 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6863
6864 // Allocate space for computed loop bounds as expected by the "init" function.
6865 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6866 Type *I32Type = Type::getInt32Ty(M.getContext());
6867 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6868 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6869 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6870 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6871 CLI->setLastIter(PLastIter);
6872
6873 // At the end of the preheader, prepare for calling the "init" function by
6874 // storing the current loop bounds into the allocated space. A canonical loop
6875 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6876 // and produces an inclusive upper bound.
6877 BasicBlock *PreHeader = CLI->getPreheader();
6878 Builder.SetInsertPoint(PreHeader->getTerminator());
6879 Constant *One = ConstantInt::get(IVTy, 1);
6880 Builder.CreateStore(One, PLowerBound);
6881 Value *UpperBound = CLI->getTripCount();
6882 Builder.CreateStore(UpperBound, PUpperBound);
6883 Builder.CreateStore(One, PStride);
6884
6885 BasicBlock *Header = CLI->getHeader();
6886 BasicBlock *Exit = CLI->getExit();
6887 BasicBlock *Cond = CLI->getCond();
6888 BasicBlock *Latch = CLI->getLatch();
6889 InsertPointTy AfterIP = CLI->getAfterIP();
6890
6891 // The CLI will be "broken" in the code below, as the loop is no longer
6892 // a valid canonical loop.
6893
6894 if (!Chunk)
6895 Chunk = One;
6896
6897 Value *ThreadNum =
6898 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6899
6900 Constant *SchedulingType =
6901 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6902
6903 // Call the "init" function.
6904 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6905 /* LowerBound */ One, UpperBound,
6906 /* step */ One, Chunk});
6907
6908 // An outer loop around the existing one.
6909 BasicBlock *OuterCond = BasicBlock::Create(
6910 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6911 PreHeader->getParent());
6912 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6913 Builder.SetInsertPoint(OuterCond, OuterCond->getFirstInsertionPt());
6915 DynamicNext,
6916 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6917 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6918 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6919 Value *LowerBound =
6920 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6921 Builder.CreateCondBr(MoreWork, Header, Exit);
6922
6923 // Change PHI-node in loop header to use outer cond rather than preheader,
6924 // and set IV to the LowerBound.
6925 Instruction *Phi = &Header->front();
6926 auto *PI = cast<PHINode>(Phi);
6927 PI->setIncomingBlock(0, OuterCond);
6928 PI->setIncomingValue(0, LowerBound);
6929
6930 // Then set the pre-header to jump to the OuterCond
6931 Instruction *Term = PreHeader->getTerminator();
6932 auto *Br = cast<UncondBrInst>(Term);
6933 Br->setSuccessor(OuterCond);
6934
6935 // Modify the inner condition:
6936 // * Use the UpperBound returned from the DynamicNext call.
6937 // * jump to the loop outer loop when done with one of the inner loops.
6938 Builder.SetInsertPoint(Cond, Cond->getFirstInsertionPt());
6939 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
6940 Instruction *Comp = &*Builder.GetInsertPoint();
6941 auto *CI = cast<CmpInst>(Comp);
6942 CI->setOperand(1, UpperBound);
6943 // Redirect the inner exit to branch to outer condition.
6944 Instruction *Branch = &Cond->back();
6945 auto *BI = cast<CondBrInst>(Branch);
6946 assert(BI->getSuccessor(1) == Exit);
6947 BI->setSuccessor(1, OuterCond);
6948
6949 // Call the "fini" function if "ordered" is present in wsloop directive.
6950 if (Ordered) {
6951 Builder.SetInsertPoint(&Latch->back());
6952 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
6953 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
6954 }
6955
6956 // Add the barrier if requested.
6957 if (NeedsBarrier) {
6958 Builder.SetInsertPoint(&Exit->back());
6959 InsertPointOrErrorTy BarrierIP =
6961 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6962 /* CheckCancelFlag */ false);
6963 if (!BarrierIP)
6964 return BarrierIP.takeError();
6965 }
6966
6967 CLI->invalidate();
6968 return AfterIP;
6969}
6970
6971/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
6972/// after this \p OldTarget will be orphaned.
6974 BasicBlock *NewTarget, DebugLoc DL) {
6975 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
6976 redirectTo(Pred, NewTarget, DL);
6977}
6978
6980 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
6981 // We add a block to BBsToKeep iff we have proven it has an external use.
6983
6984 while (true) {
6985 bool Changed = false;
6986
6987 for (BasicBlock *BB : BBs) {
6988 if (BBsToKeep.contains(BB))
6989 continue;
6990
6991 for (Use &U : BB->uses()) {
6992 auto *UseInst = dyn_cast<Instruction>(U.getUser());
6993 if (!UseInst)
6994 continue;
6995 BasicBlock *UseBB = UseInst->getParent();
6996 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
6997 BBsToKeep.insert(BB);
6998 Changed = true;
6999 break;
7000 }
7001 }
7002 }
7003
7004 if (!Changed)
7005 break;
7006 }
7007
7009 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
7010 DeleteDeadBlocks(BBsToDelete);
7011}
7012
7013CanonicalLoopInfo *
7015 InsertPointTy ComputeIP) {
7016 assert(Loops.size() >= 1 && "At least one loop required");
7017 size_t NumLoops = Loops.size();
7018
7019 // Nothing to do if there is already just one loop.
7020 if (NumLoops == 1)
7021 return Loops.front();
7022
7023 CanonicalLoopInfo *Outermost = Loops.front();
7024 CanonicalLoopInfo *Innermost = Loops.back();
7025 BasicBlock *OrigPreheader = Outermost->getPreheader();
7026 BasicBlock *OrigAfter = Outermost->getAfter();
7027 Function *F = OrigPreheader->getParent();
7028
7029 // Loop control blocks that may become orphaned later.
7030 SmallVector<BasicBlock *, 12> OldControlBBs;
7031 OldControlBBs.reserve(6 * Loops.size());
7033 Loop->collectControlBlocks(OldControlBBs);
7034
7035 // Setup the IRBuilder for inserting the trip count computation.
7036 Builder.SetCurrentDebugLocation(DL);
7037 if (ComputeIP.isSet())
7038 Builder.restoreIP(ComputeIP);
7039 else
7040 Builder.restoreIP(Outermost->getPreheaderIP());
7041
7042 // Derive the collapsed' loop trip count.
7043 // TODO: Find common/largest indvar type.
7044 Value *CollapsedTripCount = nullptr;
7045 for (CanonicalLoopInfo *L : Loops) {
7046 assert(L->isValid() &&
7047 "All loops to collapse must be valid canonical loops");
7048 Value *OrigTripCount = L->getTripCount();
7049 if (!CollapsedTripCount) {
7050 CollapsedTripCount = OrigTripCount;
7051 continue;
7052 }
7053
7054 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7055 CollapsedTripCount =
7056 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7057 }
7058
7059 // Create the collapsed loop control flow.
7060 CanonicalLoopInfo *Result =
7061 createLoopSkeleton(DL, CollapsedTripCount, F,
7062 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7063 /*IsCollapsed=*/true);
7064
7065 // Build the collapsed loop body code.
7066 // Start with deriving the input loop induction variables from the collapsed
7067 // one, using a divmod scheme. To preserve the original loops' order, the
7068 // innermost loop use the least significant bits.
7069 Builder.restoreIP(Result->getBodyIP());
7070
7071 Value *Leftover = Result->getIndVar();
7072 SmallVector<Value *> NewIndVars;
7073 NewIndVars.resize(NumLoops);
7074 for (int i = NumLoops - 1; i >= 1; --i) {
7075 Value *OrigTripCount = Loops[i]->getTripCount();
7076
7077 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7078 NewIndVars[i] = NewIndVar;
7079
7080 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7081 }
7082 // Outermost loop gets all the remaining bits.
7083 NewIndVars[0] = Leftover;
7084
7085 // Construct the loop body control flow.
7086 // We progressively construct the branch structure following in direction of
7087 // the control flow, from the leading in-between code, the loop nest body, the
7088 // trailing in-between code, and rejoining the collapsed loop's latch.
7089 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7090 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7091 // its predecessors as sources.
7092 BasicBlock *ContinueBlock = Result->getBody();
7093 BasicBlock *ContinuePred = nullptr;
7094 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7095 BasicBlock *NextSrc) {
7096 if (ContinueBlock)
7097 redirectTo(ContinueBlock, Dest, DL);
7098 else
7099 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7100
7101 ContinueBlock = nullptr;
7102 ContinuePred = NextSrc;
7103 };
7104
7105 // The code before the nested loop of each level.
7106 // Because we are sinking it into the nest, it will be executed more often
7107 // that the original loop. More sophisticated schemes could keep track of what
7108 // the in-between code is and instantiate it only once per thread.
7109 for (size_t i = 0; i < NumLoops - 1; ++i)
7110 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7111
7112 // Connect the loop nest body.
7113 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7114
7115 // The code after the nested loop at each level.
7116 for (size_t i = NumLoops - 1; i > 0; --i)
7117 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7118
7119 // Connect the finished loop to the collapsed loop latch.
7120 ContinueWith(Result->getLatch(), nullptr);
7121
7122 // Replace the input loops with the new collapsed loop.
7123 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7124 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7125
7126 // Replace the input loop indvars with the derived ones.
7127 for (size_t i = 0; i < NumLoops; ++i)
7128 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7129
7130 // Remove unused parts of the input loops.
7131 removeUnusedBlocksFromParent(OldControlBBs);
7132
7133 for (CanonicalLoopInfo *L : Loops)
7134 L->invalidate();
7135
7136#ifndef NDEBUG
7137 Result->assertOK();
7138#endif
7139 return Result;
7140}
7141
7142std::vector<CanonicalLoopInfo *>
7144 ArrayRef<Value *> TileSizes) {
7145 assert(TileSizes.size() == Loops.size() &&
7146 "Must pass as many tile sizes as there are loops");
7147 int NumLoops = Loops.size();
7148 assert(NumLoops >= 1 && "At least one loop to tile required");
7149
7150 CanonicalLoopInfo *OutermostLoop = Loops.front();
7151 CanonicalLoopInfo *InnermostLoop = Loops.back();
7152 Function *F = OutermostLoop->getBody()->getParent();
7153 BasicBlock *InnerEnter = InnermostLoop->getBody();
7154 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7155
7156 // Loop control blocks that may become orphaned later.
7157 SmallVector<BasicBlock *, 12> OldControlBBs;
7158 OldControlBBs.reserve(6 * Loops.size());
7160 Loop->collectControlBlocks(OldControlBBs);
7161
7162 // Collect original trip counts and induction variable to be accessible by
7163 // index. Also, the structure of the original loops is not preserved during
7164 // the construction of the tiled loops, so do it before we scavenge the BBs of
7165 // any original CanonicalLoopInfo.
7166 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7167 for (CanonicalLoopInfo *L : Loops) {
7168 assert(L->isValid() && "All input loops must be valid canonical loops");
7169 OrigTripCounts.push_back(L->getTripCount());
7170 OrigIndVars.push_back(L->getIndVar());
7171 }
7172
7173 // Collect the code between loop headers. These may contain SSA definitions
7174 // that are used in the loop nest body. To be usable with in the innermost
7175 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7176 // these instructions may be executed more often than before the tiling.
7177 // TODO: It would be sufficient to only sink them into body of the
7178 // corresponding tile loop.
7180 for (int i = 0; i < NumLoops - 1; ++i) {
7181 CanonicalLoopInfo *Surrounding = Loops[i];
7182 CanonicalLoopInfo *Nested = Loops[i + 1];
7183
7184 BasicBlock *EnterBB = Surrounding->getBody();
7185 BasicBlock *ExitBB = Nested->getHeader();
7186 InbetweenCode.emplace_back(EnterBB, ExitBB);
7187 }
7188
7189 // Compute the trip counts of the floor loops.
7190 Builder.SetCurrentDebugLocation(DL);
7191 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7192 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7193 for (int i = 0; i < NumLoops; ++i) {
7194 Value *TileSize = TileSizes[i];
7195 Value *OrigTripCount = OrigTripCounts[i];
7196 Type *IVType = OrigTripCount->getType();
7197
7198 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7199 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7200
7201 // 0 if tripcount divides the tilesize, 1 otherwise.
7202 // 1 means we need an additional iteration for a partial tile.
7203 //
7204 // Unfortunately we cannot just use the roundup-formula
7205 // (tripcount + tilesize - 1)/tilesize
7206 // because the summation might overflow. We do not want introduce undefined
7207 // behavior when the untiled loop nest did not.
7208 Value *FloorTripOverflow =
7209 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7210
7211 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7212 Value *FloorTripCount =
7213 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7214 "omp_floor" + Twine(i) + ".tripcount", true);
7215
7216 // Remember some values for later use.
7217 FloorCompleteCount.push_back(FloorCompleteTripCount);
7218 FloorCount.push_back(FloorTripCount);
7219 FloorRems.push_back(FloorTripRem);
7220 }
7221
7222 // Generate the new loop nest, from the outermost to the innermost.
7223 std::vector<CanonicalLoopInfo *> Result;
7224 Result.reserve(NumLoops * 2);
7225
7226 // The basic block of the surrounding loop that enters the nest generated
7227 // loop.
7228 BasicBlock *Enter = OutermostLoop->getPreheader();
7229
7230 // The basic block of the surrounding loop where the inner code should
7231 // continue.
7232 BasicBlock *Continue = OutermostLoop->getAfter();
7233
7234 // Where the next loop basic block should be inserted.
7235 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7236
7237 auto EmbeddNewLoop =
7238 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7239 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7240 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7241 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7242 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7243 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7244
7245 // Setup the position where the next embedded loop connects to this loop.
7246 Enter = EmbeddedLoop->getBody();
7247 Continue = EmbeddedLoop->getLatch();
7248 OutroInsertBefore = EmbeddedLoop->getLatch();
7249 return EmbeddedLoop;
7250 };
7251
7252 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7253 const Twine &NameBase) {
7254 for (auto P : enumerate(TripCounts)) {
7255 CanonicalLoopInfo *EmbeddedLoop =
7256 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7257 Result.push_back(EmbeddedLoop);
7258 }
7259 };
7260
7261 EmbeddNewLoops(FloorCount, "floor");
7262
7263 // Within the innermost floor loop, emit the code that computes the tile
7264 // sizes.
7265 Builder.SetInsertPoint(Enter->getTerminator());
7266 SmallVector<Value *, 4> TileCounts;
7267 for (int i = 0; i < NumLoops; ++i) {
7268 CanonicalLoopInfo *FloorLoop = Result[i];
7269 Value *TileSize = TileSizes[i];
7270
7271 Value *FloorIsEpilogue =
7272 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7273 Value *TileTripCount =
7274 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7275
7276 TileCounts.push_back(TileTripCount);
7277 }
7278
7279 // Create the tile loops.
7280 EmbeddNewLoops(TileCounts, "tile");
7281
7282 // Insert the inbetween code into the body.
7283 BasicBlock *BodyEnter = Enter;
7284 BasicBlock *BodyEntered = nullptr;
7285 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7286 BasicBlock *EnterBB = P.first;
7287 BasicBlock *ExitBB = P.second;
7288
7289 if (BodyEnter)
7290 redirectTo(BodyEnter, EnterBB, DL);
7291 else
7292 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7293
7294 BodyEnter = nullptr;
7295 BodyEntered = ExitBB;
7296 }
7297
7298 // Append the original loop nest body into the generated loop nest body.
7299 if (BodyEnter)
7300 redirectTo(BodyEnter, InnerEnter, DL);
7301 else
7302 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7304
7305 // Replace the original induction variable with an induction variable computed
7306 // from the tile and floor induction variables.
7307 Builder.restoreIP(Result.back()->getBodyIP());
7308 for (int i = 0; i < NumLoops; ++i) {
7309 CanonicalLoopInfo *FloorLoop = Result[i];
7310 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7311 Value *OrigIndVar = OrigIndVars[i];
7312 Value *Size = TileSizes[i];
7313
7314 Value *Scale =
7315 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7316 Value *Shift =
7317 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7318 OrigIndVar->replaceAllUsesWith(Shift);
7319 }
7320
7321 // Remove unused parts of the original loops.
7322 removeUnusedBlocksFromParent(OldControlBBs);
7323
7324 for (CanonicalLoopInfo *L : Loops)
7325 L->invalidate();
7326
7327#ifndef NDEBUG
7328 for (CanonicalLoopInfo *GenL : Result)
7329 GenL->assertOK();
7330#endif
7331 return Result;
7332}
7333
7334/// Attach metadata \p Properties to the basic block described by \p BB. If the
7335/// basic block already has metadata, the basic block properties are appended.
7338 // Nothing to do if no property to attach.
7339 if (Properties.empty())
7340 return;
7341
7342 LLVMContext &Ctx = BB->getContext();
7343 SmallVector<Metadata *> NewProperties;
7344 NewProperties.push_back(nullptr);
7345
7346 // If the basic block already has metadata, prepend it to the new metadata.
7347 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7348 if (Existing)
7349 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7350
7351 append_range(NewProperties, Properties);
7352 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7353 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7354
7355 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7356}
7357
7358/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7359/// loop already has metadata, the loop properties are appended.
7362 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7363
7364 // Attach metadata to the loop's latch
7365 BasicBlock *Latch = Loop->getLatch();
7366 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7368}
7369
7370/// Attach llvm.access.group metadata to the memref instructions of \p Block
7372 LoopInfo &LI) {
7373 for (Instruction &I : *Block) {
7374 if (I.mayReadOrWriteMemory()) {
7375 // TODO: This instruction may already have access group from
7376 // other pragmas e.g. #pragma clang loop vectorize. Append
7377 // so that the existing metadata is not overwritten.
7378 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7379 }
7380 }
7381}
7382
7383CanonicalLoopInfo *
7385 CanonicalLoopInfo *firstLoop = Loops.front();
7386 CanonicalLoopInfo *lastLoop = Loops.back();
7387 Function *F = firstLoop->getPreheader()->getParent();
7388
7389 // Loop control blocks that will become orphaned later
7390 SmallVector<BasicBlock *> oldControlBBs;
7392 Loop->collectControlBlocks(oldControlBBs);
7393
7394 // Collect original trip counts
7395 SmallVector<Value *> origTripCounts;
7396 for (CanonicalLoopInfo *L : Loops) {
7397 assert(L->isValid() && "All input loops must be valid canonical loops");
7398 origTripCounts.push_back(L->getTripCount());
7399 }
7400
7401 Builder.SetCurrentDebugLocation(DL);
7402
7403 // Compute max trip count.
7404 // The fused loop will be from 0 to max(origTripCounts)
7405 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7406 F, firstLoop->getHeader());
7407 Builder.SetInsertPoint(TCBlock);
7408 Value *fusedTripCount = nullptr;
7409 for (CanonicalLoopInfo *L : Loops) {
7410 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7411 Value *origTripCount = L->getTripCount();
7412 if (!fusedTripCount) {
7413 fusedTripCount = origTripCount;
7414 continue;
7415 }
7416 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7417 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7418 ".omp.fuse.tc");
7419 }
7420
7421 // Generate new loop
7422 CanonicalLoopInfo *fused =
7423 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7424 lastLoop->getLatch(), "fused");
7425
7426 // Replace original loops with the fused loop
7427 // Preheader and After are not considered inside the CLI.
7428 // These are used to compute the individual TCs of the loops
7429 // so they have to be put before the resulting fused loop.
7430 // Moving them up for readability.
7431 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7432 Loops[i]->getPreheader()->moveBefore(TCBlock);
7433 Loops[i]->getAfter()->moveBefore(TCBlock);
7434 }
7435 lastLoop->getPreheader()->moveBefore(TCBlock);
7436
7437 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7438 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7439 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7440 }
7441 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7442 redirectTo(TCBlock, fused->getPreheader(), DL);
7443 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7444
7445 // Build the fused body
7446 // Create new Blocks with conditions that jump to the original loop bodies
7448 SmallVector<Value *> condValues;
7449 for (size_t i = 0; i < Loops.size(); ++i) {
7450 BasicBlock *condBlock = BasicBlock::Create(
7451 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7452 Builder.SetInsertPoint(condBlock);
7453 Value *condValue =
7454 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7455 condBBs.push_back(condBlock);
7456 condValues.push_back(condValue);
7457 }
7458 // Join the condition blocks with the bodies of the original loops
7459 redirectTo(fused->getBody(), condBBs[0], DL);
7460 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7461 Builder.SetInsertPoint(condBBs[i]);
7462 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7463 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7464 // Replace the IV with the fused IV
7465 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7466 }
7467 // Last body jumps to the created end body block
7468 Builder.SetInsertPoint(condBBs.back());
7469 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7470 fused->getLatch());
7471 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7472 // Replace the IV with the fused IV
7473 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7474
7475 // The loop latch must have only one predecessor. Currently it is branched to
7476 // from both the last condition block and the last loop body
7477 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7478 "omp.fused.pre_latch");
7479
7480 // Remove unused parts
7481 removeUnusedBlocksFromParent(oldControlBBs);
7482
7483 // Invalidate old CLIs
7484 for (CanonicalLoopInfo *L : Loops)
7485 L->invalidate();
7486
7487#ifndef NDEBUG
7488 fused->assertOK();
7489#endif
7490 return fused;
7491}
7492
7494 LLVMContext &Ctx = Builder.getContext();
7496 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7497 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7498}
7499
7501 LLVMContext &Ctx = Builder.getContext();
7503 Loop, {
7504 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7505 });
7506}
7507
7508void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7509 Value *IfCond, ValueToValueMapTy &VMap,
7510 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7511 const Twine &NamePrefix) {
7512 Function *F = CanonicalLoop->getFunction();
7513
7514 // We can't do
7515 // if (cond) {
7516 // simd_loop;
7517 // } else {
7518 // non_simd_loop;
7519 // }
7520 // because then the CanonicalLoopInfo would only point to one of the loops:
7521 // leading to other constructs operating on the same loop to malfunction.
7522 // Instead generate
7523 // while (...) {
7524 // if (cond) {
7525 // simd_body;
7526 // } else {
7527 // not_simd_body;
7528 // }
7529 // }
7530 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7531 // body at -O3
7532
7533 // Define where if branch should be inserted
7534 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7535
7536 // Create additional blocks for the if statement
7537 BasicBlock *Cond = SplitBeforeIt->getParent();
7538 llvm::LLVMContext &C = Cond->getContext();
7540 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7542 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7543
7544 // Create if condition branch.
7545 Builder.SetInsertPoint(SplitBeforeIt);
7546 Instruction *BrInstr =
7547 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7548 InsertPointTy IP{BrInstr->getParent(), ++BrInstr->getIterator()};
7549 // Then block contains branch to omp loop body which needs to be vectorized
7550 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7551 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7552
7553 Builder.SetInsertPoint(ElseBlock);
7554
7555 // Clone loop for the else branch
7557
7558 SmallVector<BasicBlock *, 8> ExistingBlocks;
7559 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7560 ExistingBlocks.push_back(ThenBlock);
7561 ExistingBlocks.append(L->block_begin(), L->block_end());
7562 // Cond is the block that has the if clause condition
7563 // LoopCond is omp_loop.cond
7564 // LoopHeader is omp_loop.header
7565 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7566 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7567 assert(LoopCond && LoopHeader && "Invalid loop structure");
7568 for (BasicBlock *Block : ExistingBlocks) {
7569 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7570 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7571 continue;
7572 }
7573 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7574
7575 // fix name not to be omp.if.then
7576 if (Block == ThenBlock)
7577 NewBB->setName(NamePrefix + ".if.else");
7578
7579 NewBB->moveBefore(CanonicalLoop->getExit());
7580 VMap[Block] = NewBB;
7581 NewBlocks.push_back(NewBB);
7582 }
7583 remapInstructionsInBlocks(NewBlocks, VMap);
7584 Builder.CreateBr(NewBlocks.front());
7585
7586 // The loop latch must have only one predecessor. Currently it is branched to
7587 // from both the 'then' and 'else' branches.
7588 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7589 NamePrefix + ".pre_latch");
7590
7591 // Ensure that the then block is added to the loop so we add the attributes in
7592 // the next step
7593 L->addBasicBlockToLoop(ThenBlock, LI);
7594}
7595
7596unsigned
7598 const StringMap<bool> &Features) {
7599 if (TargetTriple.isX86()) {
7600 if (Features.lookup("avx512f"))
7601 return 512;
7602 else if (Features.lookup("avx"))
7603 return 256;
7604 return 128;
7605 }
7606 if (TargetTriple.isPPC())
7607 return 128;
7608 if (TargetTriple.isWasm())
7609 return 128;
7610 if (TargetTriple.isSystemZ())
7611 return 64;
7612 return 0;
7613}
7614
7616 MapVector<Value *, Value *> AlignedVars,
7617 Value *IfCond, OrderKind Order,
7618 ConstantInt *Simdlen, ConstantInt *Safelen) {
7619 LLVMContext &Ctx = Builder.getContext();
7620
7621 Function *F = CanonicalLoop->getFunction();
7622
7623 // Blocks must have terminators.
7624 // FIXME: Don't run analyses on incomplete/invalid IR.
7626 for (BasicBlock &BB : *F)
7627 if (!BB.hasTerminator())
7628 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7629
7630 // TODO: We should not rely on pass manager. Currently we use pass manager
7631 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7632 // object. We should have a method which returns all blocks between
7633 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7635 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7636 FAM.registerPass([]() { return LoopAnalysis(); });
7637 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7638
7639 LoopAnalysis LIA;
7640 LoopInfo &&LI = LIA.run(*F, FAM);
7641
7642 for (Instruction *I : UIs)
7643 I->eraseFromParent();
7644
7645 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7646 if (AlignedVars.size()) {
7647 InsertPointTy IP = Builder.saveIP();
7648 for (auto &AlignedItem : AlignedVars) {
7649 Value *AlignedPtr = AlignedItem.first;
7650 Value *Alignment = AlignedItem.second;
7651 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7652 Builder.SetInsertPoint(loadInst->getNextNode());
7653 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7654 Alignment);
7655 }
7656 Builder.restoreIP(IP);
7657 }
7658
7659 if (IfCond) {
7660 ValueToValueMapTy VMap;
7661 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7662 }
7663
7665
7666 // Get the basic blocks from the loop in which memref instructions
7667 // can be found.
7668 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7669 // preferably without running any passes.
7670 for (BasicBlock *Block : L->getBlocks()) {
7671 if (Block == CanonicalLoop->getCond() ||
7672 Block == CanonicalLoop->getHeader())
7673 continue;
7674 Reachable.insert(Block);
7675 }
7676
7677 SmallVector<Metadata *> LoopMDList;
7678
7679 // In presence of finite 'safelen', it may be unsafe to mark all
7680 // the memory instructions parallel, because loop-carried
7681 // dependences of 'safelen' iterations are possible.
7682 // If clause order(concurrent) is specified then the memory instructions
7683 // are marked parallel even if 'safelen' is finite.
7684 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7685 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7686
7687 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7688 // versions so we can't add the loop attributes in that case.
7689 if (IfCond) {
7690 // we can still add llvm.loop.parallel_access
7691 addLoopMetadata(CanonicalLoop, LoopMDList);
7692 return;
7693 }
7694
7695 // Use the above access group metadata to create loop level
7696 // metadata, which should be distinct for each loop.
7697 LoopMDList.push_back(
7698 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7699
7700 if (Simdlen || Safelen) {
7701 // If both simdlen and safelen clauses are specified, the value of the
7702 // simdlen parameter must be less than or equal to the value of the safelen
7703 // parameter. Therefore, use safelen only in the absence of simdlen.
7704 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7705 LoopMDList.push_back(
7706 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7707 ConstantAsMetadata::get(VectorizeWidth)}));
7708 }
7709
7710 addLoopMetadata(CanonicalLoop, LoopMDList);
7711}
7712
7713/// Create the TargetMachine object to query the backend for optimization
7714/// preferences.
7715///
7716/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7717/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7718/// needed for the LLVM pass pipline. We use some default options to avoid
7719/// having to pass too many settings from the frontend that probably do not
7720/// matter.
7721///
7722/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7723/// method. If we are going to use TargetMachine for more purposes, especially
7724/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7725/// might become be worth requiring front-ends to pass on their TargetMachine,
7726/// or at least cache it between methods. Note that while fontends such as Clang
7727/// have just a single main TargetMachine per translation unit, "target-cpu" and
7728/// "target-features" that determine the TargetMachine are per-function and can
7729/// be overrided using __attribute__((target("OPTIONS"))).
7730static std::unique_ptr<TargetMachine>
7732 Module *M = F->getParent();
7733
7734 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7735 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7736 const llvm::Triple &Triple = M->getTargetTriple();
7737
7738 std::string Error;
7740 if (!TheTarget)
7741 return {};
7742
7744 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7745 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7746 /*CodeModel=*/std::nullopt, OptLevel));
7747}
7748
7749/// Heuristically determine the best-performant unroll factor for \p CLI. This
7750/// depends on the target processor. We are re-using the same heuristics as the
7751/// LoopUnrollPass.
7753 Function *F = CLI->getFunction();
7754
7755 // Assume the user requests the most aggressive unrolling, even if the rest of
7756 // the code is optimized using a lower setting.
7758 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7759
7760 // Blocks must have terminators.
7761 // FIXME: Don't run analyses on incomplete/invalid IR.
7763 for (BasicBlock &BB : *F)
7764 if (!BB.hasTerminator())
7765 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7766
7768 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7769 FAM.registerPass([]() { return AssumptionAnalysis(); });
7770 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7771 FAM.registerPass([]() { return LoopAnalysis(); });
7772 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7773 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7774 TargetIRAnalysis TIRA;
7775 if (TM)
7776 TIRA = TargetIRAnalysis(
7777 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7778 FAM.registerPass([&]() { return TIRA; });
7779
7780 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7782 ScalarEvolution &&SE = SEA.run(*F, FAM);
7784 DominatorTree &&DT = DTA.run(*F, FAM);
7785 LoopAnalysis LIA;
7786 LoopInfo &&LI = LIA.run(*F, FAM);
7788 AssumptionCache &&AC = ACT.run(*F, FAM);
7790
7791 for (Instruction *I : UIs)
7792 I->eraseFromParent();
7793
7794 Loop *L = LI.getLoopFor(CLI->getHeader());
7795 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7796
7798 L, SE, TTI,
7799 /*BlockFrequencyInfo=*/nullptr,
7800 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7801 /*UserThreshold=*/std::nullopt,
7802 /*UserAllowPartial=*/true,
7803 /*UserAllowRuntime=*/true,
7804 /*UserUpperBound=*/std::nullopt,
7805 /*UserFullUnrollMaxCount=*/std::nullopt);
7806
7807 UP.Force = true;
7808
7809 // Account for additional optimizations taking place before the LoopUnrollPass
7810 // would unroll the loop.
7813
7814 // Use normal unroll factors even if the rest of the code is optimized for
7815 // size.
7818
7819 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7820 << " Threshold=" << UP.Threshold << "\n"
7821 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7822 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7823 << " PartialOptSizeThreshold="
7824 << UP.PartialOptSizeThreshold << "\n");
7825
7826 // Disable peeling.
7829 /*UserAllowPeeling=*/false,
7830 /*UserAllowProfileBasedPeeling=*/false,
7831 /*UnrollingSpecficValues=*/false);
7832
7834 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7835
7836 // Assume that reads and writes to stack variables can be eliminated by
7837 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7838 // size.
7839 for (BasicBlock *BB : L->blocks()) {
7840 for (Instruction &I : *BB) {
7841 Value *Ptr;
7842 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7843 Ptr = Load->getPointerOperand();
7844 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7845 Ptr = Store->getPointerOperand();
7846 } else
7847 continue;
7848
7849 Ptr = Ptr->stripPointerCasts();
7850
7851 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7852 if (Alloca->getParent() == &F->getEntryBlock())
7853 EphValues.insert(&I);
7854 }
7855 }
7856 }
7857
7858 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7859
7860 // Loop is not unrollable if the loop contains certain instructions.
7861 if (!UCE.canUnroll()) {
7862 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7863 return 1;
7864 }
7865
7866 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7867 << "\n");
7868
7869 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7870 // be able to use it.
7871 int TripCount = 0;
7872 int MaxTripCount = 0;
7873 bool MaxOrZero = false;
7874 unsigned TripMultiple = 0;
7875
7876 unsigned Factor =
7877 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7878 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7879 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7880
7881 // This function returns 1 to signal to not unroll a loop.
7882 if (Factor == 0)
7883 return 1;
7884 return Factor;
7885}
7886
7888 int32_t Factor,
7889 CanonicalLoopInfo **UnrolledCLI) {
7890 assert(Factor >= 0 && "Unroll factor must not be negative");
7891
7892 Function *F = Loop->getFunction();
7893 LLVMContext &Ctx = F->getContext();
7894
7895 // If the unrolled loop is not used for another loop-associated directive, it
7896 // is sufficient to add metadata for the LoopUnrollPass.
7897 if (!UnrolledCLI) {
7898 SmallVector<Metadata *, 2> LoopMetadata;
7899 LoopMetadata.push_back(
7900 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7901
7902 if (Factor >= 1) {
7904 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7905 LoopMetadata.push_back(MDNode::get(
7906 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7907 }
7908
7909 addLoopMetadata(Loop, LoopMetadata);
7910 return;
7911 }
7912
7913 // Heuristically determine the unroll factor.
7914 if (Factor == 0)
7916
7917 // No change required with unroll factor 1.
7918 if (Factor == 1) {
7919 *UnrolledCLI = Loop;
7920 return;
7921 }
7922
7923 assert(Factor >= 2 &&
7924 "unrolling only makes sense with a factor of 2 or larger");
7925
7926 Type *IndVarTy = Loop->getIndVarType();
7927
7928 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7929 // unroll the inner loop.
7930 Value *FactorVal =
7931 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7932 /*isSigned=*/false));
7933 std::vector<CanonicalLoopInfo *> LoopNest =
7934 tileLoops(DL, {Loop}, {FactorVal});
7935 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7936 *UnrolledCLI = LoopNest[0];
7937 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7938
7939 // LoopUnrollPass can only fully unroll loops with constant trip count.
7940 // Unroll by the unroll factor with a fallback epilog for the remainder
7941 // iterations if necessary.
7943 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7945 InnerLoop,
7946 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7948 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
7949
7950#ifndef NDEBUG
7951 (*UnrolledCLI)->assertOK();
7952#endif
7953}
7954
7957 llvm::Value *BufSize, llvm::Value *CpyBuf,
7958 llvm::Value *CpyFn, llvm::Value *DidIt) {
7959 if (!updateToLocation(Loc))
7960 return Loc.IP;
7961
7962 uint32_t SrcLocStrSize;
7963 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7964 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7965 Value *ThreadId = getOrCreateThreadID(Ident);
7966
7967 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
7968
7969 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
7970
7971 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
7972 createRuntimeFunctionCall(Fn, Args);
7973
7974 return Builder.saveIP();
7975}
7976
7978 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7979 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
7981
7982 if (!updateToLocation(Loc))
7983 return Loc.IP;
7984
7985 // If needed allocate and initialize `DidIt` with 0.
7986 // DidIt: flag variable: 1=single thread; 0=not single thread.
7987 llvm::Value *DidIt = nullptr;
7988 if (!CPVars.empty()) {
7989 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
7990 Builder.CreateStore(Builder.getInt32(0), DidIt);
7991 }
7992
7993 Directive OMPD = Directive::OMPD_single;
7994 uint32_t SrcLocStrSize;
7995 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7996 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7997 Value *ThreadId = getOrCreateThreadID(Ident);
7998 Value *Args[] = {Ident, ThreadId};
7999
8000 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
8001 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8002
8003 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
8004 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8005
8006 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
8007 if (Error Err = FiniCB(IP))
8008 return Err;
8009
8010 // The thread that executes the single region must set `DidIt` to 1.
8011 // This is used by __kmpc_copyprivate, to know if the caller is the
8012 // single thread or not.
8013 if (DidIt)
8014 Builder.CreateStore(Builder.getInt32(1), DidIt);
8015
8016 return Error::success();
8017 };
8018
8019 // generates the following:
8020 // if (__kmpc_single()) {
8021 // .... single region ...
8022 // __kmpc_end_single
8023 // }
8024 // __kmpc_copyprivate
8025 // __kmpc_barrier
8026
8027 InsertPointOrErrorTy AfterIP =
8028 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8029 /*Conditional*/ true,
8030 /*hasFinalize*/ true);
8031 if (!AfterIP)
8032 return AfterIP.takeError();
8033
8034 if (DidIt) {
8035 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8036 // NOTE BufSize is currently unused, so just pass 0.
8038 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8039 CPFuncs[I], DidIt);
8040 // NOTE __kmpc_copyprivate already inserts a barrier
8041 } else if (!IsNowait) {
8042 InsertPointOrErrorTy AfterIP =
8044 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8045 /* CheckCancelFlag */ false);
8046 if (!AfterIP)
8047 return AfterIP.takeError();
8048 }
8049 return Builder.saveIP();
8050}
8051
8054 BodyGenCallbackTy BodyGenCB,
8055 FinalizeCallbackTy FiniCB, bool IsNowait) {
8056
8057 if (!updateToLocation(Loc))
8058 return Loc.IP;
8059
8060 // All threads execute the scope body — no conditional entry.
8061 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8062 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8063 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8064 /*IsCancellable=*/false);
8065 if (!AfterIP)
8066 return AfterIP.takeError();
8067
8068 Builder.restoreIP(*AfterIP);
8069 if (!IsNowait) {
8070 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8071 omp::Directive::OMPD_unknown,
8072 /*ForceSimpleCall=*/false,
8073 /*CheckCancelFlag=*/false);
8074 if (!AfterIP)
8075 return AfterIP.takeError();
8076 }
8077 return Builder.saveIP();
8078}
8079
8081 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8082 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8083
8084 if (!updateToLocation(Loc))
8085 return Loc.IP;
8086
8087 Directive OMPD = Directive::OMPD_critical;
8088 uint32_t SrcLocStrSize;
8089 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8090 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8091 Value *ThreadId = getOrCreateThreadID(Ident);
8092 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8093 Value *Args[] = {Ident, ThreadId, LockVar};
8094
8095 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8096 Function *RTFn = nullptr;
8097 if (HintInst) {
8098 // Add Hint to entry Args and create call
8099 EnterArgs.push_back(HintInst);
8100 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8101 } else {
8102 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8103 }
8104 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8105
8106 Function *ExitRTLFn =
8107 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8108 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8109
8110 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8111 /*Conditional*/ false, /*hasFinalize*/ true);
8112}
8113
8116 InsertPointTy AllocaIP, unsigned NumLoops,
8117 ArrayRef<llvm::Value *> StoreValues,
8118 const Twine &Name, bool IsDependSource) {
8119 assert(
8120 llvm::all_of(StoreValues,
8121 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8122 "OpenMP runtime requires depend vec with i64 type");
8123
8124 if (!updateToLocation(Loc))
8125 return Loc.IP;
8126
8127 // Allocate space for vector and generate alloc instruction.
8128 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8129 Builder.restoreIP(AllocaIP);
8130 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8131 ArgsBase->setAlignment(Align(8));
8133
8134 // Store the index value with offset in depend vector.
8135 for (unsigned I = 0; I < NumLoops; ++I) {
8136 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8137 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8138 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8139 STInst->setAlignment(Align(8));
8140 }
8141
8142 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8143 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8144
8145 uint32_t SrcLocStrSize;
8146 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8147 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8148 Value *ThreadId = getOrCreateThreadID(Ident);
8149 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8150
8151 Function *RTLFn = nullptr;
8152 if (IsDependSource)
8153 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8154 else
8155 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8156 createRuntimeFunctionCall(RTLFn, Args);
8157
8158 return Builder.saveIP();
8159}
8160
8162 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8163 FinalizeCallbackTy FiniCB, bool IsThreads) {
8164 if (!updateToLocation(Loc))
8165 return Loc.IP;
8166
8167 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8168 Instruction *EntryCall = nullptr;
8169 Instruction *ExitCall = nullptr;
8170
8171 if (IsThreads) {
8172 uint32_t SrcLocStrSize;
8173 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8174 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8175 Value *ThreadId = getOrCreateThreadID(Ident);
8176 Value *Args[] = {Ident, ThreadId};
8177
8178 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8179 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8180
8181 Function *ExitRTLFn =
8182 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8183 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8184 }
8185
8186 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8187 /*Conditional*/ false, /*hasFinalize*/ true);
8188}
8189
8190OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8191 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8192 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8193 bool HasFinalize, bool IsCancellable) {
8194
8195 if (HasFinalize)
8196 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8197
8198 // Create inlined region's entry and body blocks, in preparation
8199 // for conditional creation
8200 BasicBlock *EntryBB = Builder.GetInsertBlock();
8201 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8203 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8204 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8205 BasicBlock *FiniBB =
8206 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8207
8208 Builder.SetInsertPoint(EntryBB->getTerminator());
8209 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8210
8211 // generate body
8212 if (Error Err =
8213 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8214 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8215 return Err;
8216
8217 // emit exit call and do any needed finalization.
8218 auto FinIP = InsertPointTy(FiniBB, FiniBB->getFirstInsertionPt());
8219 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8220 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8221 "Unexpected control flow graph state!!");
8222 InsertPointOrErrorTy AfterIP =
8223 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8224 if (!AfterIP)
8225 return AfterIP.takeError();
8226
8227 // If we are skipping the region of a non conditional, remove the exit
8228 // block, and clear the builder's insertion point.
8229 assert(SplitPos->getParent() == ExitBB &&
8230 "Unexpected Insertion point location!");
8231 auto merged = MergeBlockIntoPredecessor(ExitBB);
8232 BasicBlock *ExitPredBB = SplitPos->getParent();
8233 auto InsertBB = merged ? ExitPredBB : ExitBB;
8235 SplitPos->eraseFromParent();
8236 Builder.SetInsertPoint(InsertBB);
8237
8238 return Builder.saveIP();
8239}
8240
8241OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8242 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8243 // if nothing to do, Return current insertion point.
8244 if (!Conditional || !EntryCall)
8245 return Builder.saveIP();
8246
8247 BasicBlock *EntryBB = Builder.GetInsertBlock();
8248 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8249 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8250 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8251
8252 // Emit thenBB and set the Builder's insertion point there for
8253 // body generation next. Place the block after the current block.
8254 Function *CurFn = EntryBB->getParent();
8255 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8256
8257 // Move Entry branch to end of ThenBB, and replace with conditional
8258 // branch (If-stmt)
8259 Instruction *EntryBBTI = EntryBB->getTerminator();
8260 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8261 EntryBBTI->removeFromParent();
8262 Builder.SetInsertPoint(UI);
8263 Builder.Insert(EntryBBTI);
8264 UI->eraseFromParent();
8265 Builder.SetInsertPoint(ThenBB->getTerminator());
8266
8267 // return an insertion point to ExitBB.
8268 return IRBuilder<>::InsertPoint(ExitBB, ExitBB->getFirstInsertionPt());
8269}
8270
8271OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8272 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8273 bool HasFinalize) {
8274
8275 Builder.restoreIP(FinIP);
8276
8277 // If there is finalization to do, emit it before the exit call
8278 if (HasFinalize) {
8279 assert(!FinalizationStack.empty() &&
8280 "Unexpected finalization stack state!");
8281
8282 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8283 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8284
8285 if (Error Err = Fi.mergeFiniBB(Builder, FinIP.getBlock()))
8286 return std::move(Err);
8287
8288 // Exit condition: insertion point is before the terminator of the new Fini
8289 // block
8290 Builder.SetInsertPoint(FinIP.getBlock()->getTerminator());
8291 }
8292
8293 if (!ExitCall)
8294 return Builder.saveIP();
8295
8296 // place the Exitcall as last instruction before Finalization block terminator
8297 ExitCall->removeFromParent();
8298 Builder.Insert(ExitCall);
8299
8300 return IRBuilder<>::InsertPoint(ExitCall->getParent(),
8301 ExitCall->getIterator());
8302}
8303
8305 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8306 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8307 if (!IP.isSet())
8308 return IP;
8309
8311
8312 // creates the following CFG structure
8313 // OMP_Entry : (MasterAddr != PrivateAddr)?
8314 // F T
8315 // | \
8316 // | copin.not.master
8317 // | /
8318 // v /
8319 // copyin.not.master.end
8320 // |
8321 // v
8322 // OMP.Entry.Next
8323
8324 BasicBlock *OMP_Entry = IP.getBlock();
8325 Function *CurFn = OMP_Entry->getParent();
8326 BasicBlock *CopyBegin =
8327 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8328 BasicBlock *CopyEnd = nullptr;
8329
8330 // If entry block is terminated, split to preserve the branch to following
8331 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8333 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8334 "copyin.not.master.end");
8335 OMP_Entry->getTerminator()->eraseFromParent();
8336 } else {
8337 CopyEnd =
8338 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8339 }
8340
8341 Builder.SetInsertPoint(OMP_Entry);
8342 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8343 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8344 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8345 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8346
8347 Builder.SetInsertPoint(CopyBegin);
8348 if (BranchtoEnd)
8349 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8350
8351 return Builder.saveIP();
8352}
8353
8355 Value *Size, Value *Allocator,
8356 std::string Name) {
8358 if (!updateToLocation(Loc))
8359 return nullptr;
8360
8361 uint32_t SrcLocStrSize;
8362 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8363 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8364 Value *ThreadId = getOrCreateThreadID(Ident);
8365 Value *Args[] = {ThreadId, Size, Allocator};
8366
8367 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8368
8369 return createRuntimeFunctionCall(Fn, Args, Name);
8370}
8371
8373 Value *Align, Value *Size,
8374 Value *Allocator,
8375 std::string Name) {
8377 if (!updateToLocation(Loc))
8378 return nullptr;
8379
8380 uint32_t SrcLocStrSize;
8381 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8382 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8383 Value *ThreadId = getOrCreateThreadID(Ident);
8384 Value *Args[] = {ThreadId, Align, Size, Allocator};
8385
8386 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8387
8388 return Builder.CreateCall(Fn, Args, Name);
8389}
8390
8392 Value *Addr, Value *Allocator,
8393 std::string Name) {
8395 if (!updateToLocation(Loc))
8396 return nullptr;
8397
8398 uint32_t SrcLocStrSize;
8399 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8400 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8401 Value *ThreadId = getOrCreateThreadID(Ident);
8402 Value *Args[] = {ThreadId, Addr, Allocator};
8403 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8404 return createRuntimeFunctionCall(Fn, Args, Name);
8405}
8406
8408 Value *Size,
8409 const Twine &Name) {
8412
8413 Value *Args[] = {Size};
8414 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8415 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8417 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8418 return Call;
8419}
8420
8422 Type *VarType,
8423 const Twine &Name) {
8424 return createOMPAllocShared(
8425 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8426}
8427
8429 Value *Addr, Value *Size,
8430 const Twine &Name) {
8433
8434 Value *Args[] = {Addr, Size};
8435 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8436 return Builder.CreateCall(Fn, Args, Name);
8437}
8438
8440 Value *Addr, Type *VarType,
8441 const Twine &Name) {
8442 return createOMPFreeShared(
8443 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8444 Name);
8445}
8446
8448 const LocationDescription &Loc, Value *InteropVar,
8450 Value *DependenceAddress, bool HaveNowaitClause) {
8453
8454 uint32_t SrcLocStrSize;
8455 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8456 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8457 Value *ThreadId = getOrCreateThreadID(Ident);
8458 if (Device == nullptr)
8460 else if (Device->getType() != Int32)
8461 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8462 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8463 if (NumDependences == nullptr) {
8464 NumDependences = ConstantInt::get(Int32, 0);
8465 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8466 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8467 }
8468 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8469 Value *Args[] = {
8470 Ident, ThreadId, InteropVar, InteropTypeVal,
8471 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8472
8473 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8474
8475 return createRuntimeFunctionCall(Fn, Args);
8476}
8477
8479 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8480 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8483
8484 uint32_t SrcLocStrSize;
8485 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8486 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8487 Value *ThreadId = getOrCreateThreadID(Ident);
8488 if (Device == nullptr)
8490 else if (Device->getType() != Int32)
8491 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8492 if (NumDependences == nullptr) {
8493 NumDependences = ConstantInt::get(Int32, 0);
8494 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8495 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8496 }
8497 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8498 Value *Args[] = {
8499 Ident, ThreadId, InteropVar, Device,
8500 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8501
8502 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8503
8504 return createRuntimeFunctionCall(Fn, Args);
8505}
8506
8508 Value *InteropVar, Value *Device,
8509 Value *NumDependences,
8510 Value *DependenceAddress,
8511 bool HaveNowaitClause) {
8514 uint32_t SrcLocStrSize;
8515 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8516 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8517 Value *ThreadId = getOrCreateThreadID(Ident);
8518 if (Device == nullptr)
8520 else if (Device->getType() != Int32)
8521 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8522 if (NumDependences == nullptr) {
8523 NumDependences = ConstantInt::get(Int32, 0);
8524 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8525 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8526 }
8527 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8528 Value *Args[] = {
8529 Ident, ThreadId, InteropVar, Device,
8530 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8531
8532 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8533
8534 return createRuntimeFunctionCall(Fn, Args);
8535}
8536
8539 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8542
8543 uint32_t SrcLocStrSize;
8544 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8545 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8546 Value *ThreadId = getOrCreateThreadID(Ident);
8547 Constant *ThreadPrivateCache =
8548 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8549 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8550
8551 Function *Fn =
8552 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8553
8554 return createRuntimeFunctionCall(Fn, Args);
8555}
8556
8558 const LocationDescription &Loc,
8560 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8561 "expected num_threads and num_teams to be specified");
8562
8563 if (!updateToLocation(Loc))
8564 return Loc.IP;
8565
8566 uint32_t SrcLocStrSize;
8567 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8568 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8569 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8570 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8571 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8572 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8573 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8574 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8575
8576 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8577 Function *Kernel = DebugKernelWrapper;
8578
8579 // We need to strip the debug prefix to get the correct kernel name.
8580 StringRef KernelName = Kernel->getName();
8581 const std::string DebugPrefix = "_debug__";
8582 if (KernelName.ends_with(DebugPrefix)) {
8583 KernelName = KernelName.drop_back(DebugPrefix.length());
8584 Kernel = M.getFunction(KernelName);
8585 assert(Kernel && "Expected the real kernel to exist");
8586 }
8587
8588 // Manifest the launch configuration in the metadata matching the kernel
8589 // environment.
8590 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8591 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8592 Attrs.MaxTeams.front());
8593
8594 // If MaxThreads is not set and needs adjustment, select the maximum between
8595 // the default workgroup size and the MinThreads value.
8596 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8597 if (MaxThreadsVal < 0 && UseDefaultMaxThreads) {
8598 if (hasGridValue(T)) {
8599 MaxThreadsVal =
8600 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8601 Attrs.MinThreads.front());
8602 } else {
8603 MaxThreadsVal = Attrs.MinThreads.front();
8604 }
8605 }
8606
8607 // Generic mode runs the main thread on a warp of its own, past thread_limit.
8608 // Reserve the widest warp any target has. Not on SPIR-V, causes problems with
8609 // Level Zero.
8610 if (MaxThreadsVal > 0 && Attrs.ExecFlags == omp::OMP_TGT_EXEC_MODE_GENERIC &&
8611 hasGridValue(T) && !T.isSPIRV())
8612 MaxThreadsVal = int32_t(
8613 std::min<int64_t>(int64_t(MaxThreadsVal) + 64,
8614 int64_t(getGridValue(T, Kernel).GV_Max_WG_Size)));
8615
8616 if (MaxThreadsVal > 0)
8617 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8618 MaxThreadsVal);
8619
8620 Constant *MinThreads =
8621 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8622 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8623 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8624 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8625 Constant *ReductionDataSize =
8626 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8627
8629 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8630 const DataLayout &DL = Fn->getDataLayout();
8631
8632 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8633 Constant *DynamicEnvironmentInitializer =
8634 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8635 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8636 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8637 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8638 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8639 DL.getDefaultGlobalsAddressSpace());
8640 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8641
8642 Constant *DynamicEnvironment =
8643 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8644 ? DynamicEnvironmentGV
8645 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8646 DynamicEnvironmentPtr);
8647
8648 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8649 ConfigurationEnvironment, {
8650 UseGenericStateMachineVal,
8651 MayUseNestedParallelismVal,
8652 IsSPMDVal,
8653 MinThreads,
8654 MaxThreads,
8655 MinTeams,
8656 MaxTeams,
8657 ReductionDataSize,
8658 });
8659 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8660 KernelEnvironment, {
8661 ConfigurationEnvironmentInitializer,
8662 Ident,
8663 DynamicEnvironment,
8664 });
8665 std::string KernelEnvironmentName =
8666 (KernelName + "_kernel_environment").str();
8667 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8668 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8669 KernelEnvironmentInitializer, KernelEnvironmentName,
8670 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8671 DL.getDefaultGlobalsAddressSpace());
8672 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8673
8674 Constant *KernelEnvironment =
8675 KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8676 ? KernelEnvironmentGV
8677 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8678 KernelEnvironmentPtr);
8679 Value *KernelLaunchEnvironment =
8680 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8681 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8682 KernelLaunchEnvironment =
8683 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8684 ? KernelLaunchEnvironment
8685 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8686 KernelLaunchEnvParamTy);
8687 CallInst *ThreadKind = createRuntimeFunctionCall(
8688 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8689
8690 Value *ExecUserCode = Builder.CreateICmpEQ(
8691 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8692 "exec_user_code");
8693
8694 // ThreadKind = __kmpc_target_init(...)
8695 // if (ThreadKind == -1)
8696 // user_code
8697 // else
8698 // return;
8699
8700 auto *UI = Builder.CreateUnreachable();
8701 BasicBlock *CheckBB = UI->getParent();
8702 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8703
8704 BasicBlock *WorkerExitBB = BasicBlock::Create(
8705 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8706 Builder.SetInsertPoint(WorkerExitBB);
8707 Builder.CreateRetVoid();
8708
8709 auto *CheckBBTI = CheckBB->getTerminator();
8710 Builder.SetInsertPoint(CheckBBTI);
8711 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8712
8713 CheckBBTI->eraseFromParent();
8714 UI->eraseFromParent();
8715
8716 // Continue in the "user_code" block, see diagram above and in
8717 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8718 return InsertPointTy(UserCodeEntryBB, UserCodeEntryBB->getFirstInsertionPt());
8719}
8720
8722 int32_t TeamsReductionDataSize) {
8723 if (!updateToLocation(Loc))
8724 return;
8725
8727 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8728
8730
8731 if (!TeamsReductionDataSize)
8732 return;
8733
8734 Function *Kernel = Builder.GetInsertBlock()->getParent();
8735 // We need to strip the debug prefix to get the correct kernel name.
8736 StringRef KernelName = Kernel->getName();
8737 const std::string DebugPrefix = "_debug__";
8738 if (KernelName.ends_with(DebugPrefix))
8739 KernelName = KernelName.drop_back(DebugPrefix.length());
8740 auto *KernelEnvironmentGV =
8741 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8742 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8743 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8744 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8745 KernelEnvironmentInitializer,
8746 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8747 KernelEnvironmentGV->setInitializer(NewInitializer);
8748}
8749
8750static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8751 bool Min) {
8752 if (Kernel.hasFnAttribute(Name)) {
8753 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8754 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8755 }
8756 Kernel.addFnAttr(Name, llvm::utostr(Value));
8757}
8758
8759std::pair<int32_t, int32_t>
8761 int32_t ThreadLimit =
8762 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8763
8764 if (T.isAMDGPU()) {
8765 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8766 if (!Attr.isValid() || !Attr.isStringAttribute())
8767 return {0, ThreadLimit};
8768 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8769 int32_t LB, UB;
8770 if (!llvm::to_integer(UBStr, UB, 10))
8771 return {0, ThreadLimit};
8772 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8773 if (!llvm::to_integer(LBStr, LB, 10))
8774 return {0, UB};
8775 return {LB, UB};
8776 }
8777
8778 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8779 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8780 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8781 }
8782 return {0, ThreadLimit};
8783}
8784
8786 Function &Kernel, int32_t LB,
8787 int32_t UB) {
8788 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8789
8790 if (T.isAMDGPU()) {
8791 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8792 llvm::utostr(LB) + "," + llvm::utostr(UB));
8793 return;
8794 }
8795
8797}
8798
8799std::pair<int32_t, int32_t>
8801 // TODO: Read from backend annotations if available.
8802 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8803}
8804
8806 int32_t LB, int32_t UB) {
8807 if (UB > 0) {
8808 if (T.isNVPTX())
8810 if (T.isAMDGPU())
8811 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8812 }
8813
8814 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8815}
8816
8817void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8818 Function *OutlinedFn) {
8819 if (Config.isTargetDevice()) {
8821 // TODO: Determine if DSO local can be set to true.
8822 OutlinedFn->setDSOLocal(false);
8824 if (T.isAMDGCN())
8826 else if (T.isNVPTX())
8828 else if (T.isSPIRV())
8830 }
8831}
8832
8833Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8834 StringRef EntryFnIDName) {
8835 if (Config.isTargetDevice()) {
8836 assert(OutlinedFn && "The outlined function must exist if embedded");
8837 return OutlinedFn;
8838 }
8839
8840 return new GlobalVariable(
8841 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8842 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8843}
8844
8845Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8846 StringRef EntryFnName) {
8847 if (OutlinedFn)
8848 return OutlinedFn;
8849
8850 assert(!M.getGlobalVariable(EntryFnName, true) &&
8851 "Named kernel already exists?");
8852 return new GlobalVariable(
8853 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8854 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8855}
8856
8858 TargetRegionEntryInfo &EntryInfo,
8859 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8860 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8861
8862 SmallString<64> EntryFnName;
8863 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8864
8865 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8866 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8867 if (!CBResult)
8868 return CBResult.takeError();
8869 OutlinedFn = *CBResult;
8870 } else {
8871 OutlinedFn = nullptr;
8872 }
8873
8874 // If this target outline function is not an offload entry, we don't need to
8875 // register it. This may be in the case of a false if clause, or if there are
8876 // no OpenMP targets.
8877 if (!IsOffloadEntry)
8878 return Error::success();
8879
8880 std::string EntryFnIDName =
8881 Config.isTargetDevice()
8882 ? std::string(EntryFnName)
8883 : createPlatformSpecificName({EntryFnName, "region_id"});
8884
8885 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8886 EntryFnName, EntryFnIDName);
8887 return Error::success();
8888}
8889
8891 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8892 StringRef EntryFnName, StringRef EntryFnIDName) {
8893 if (OutlinedFn)
8894 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8895 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8896 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8897 OffloadInfoManager.registerTargetRegionEntryInfo(
8898 EntryInfo, EntryAddr, OutlinedFnID,
8900 return OutlinedFnID;
8901}
8902
8904 const LocationDescription &Loc, InsertPointTy AllocaIP,
8905 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8906 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8907 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8908 omp::RuntimeFunction *MapperFunc,
8910 BodyGenTy BodyGenType)>
8911 BodyGenCB,
8912 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8913 if (!updateToLocation(Loc))
8914 return InsertPointTy();
8915
8916 Builder.restoreIP(CodeGenIP);
8917
8918 bool IsStandAlone = !BodyGenCB;
8919 MapInfosTy *MapInfo;
8920 // Generate the code for the opening of the data environment. Capture all the
8921 // arguments of the runtime call by reference because they are used in the
8922 // closing of the region.
8923 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8924 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8925 MapInfo = &GenMapInfoCB(Builder.saveIP());
8926 if (Error Err = emitOffloadingArrays(
8927 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
8928 /*IsNonContiguous=*/true, DeviceAddrCB))
8929 return Err;
8930
8931 TargetDataRTArgs RTArgs;
8933
8934 // Emit the number of elements in the offloading arrays.
8935 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8936
8937 // Source location for the ident struct
8938 if (!SrcLocInfo) {
8939 uint32_t SrcLocStrSize;
8940 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8941 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8942 }
8943
8944 SmallVector<llvm::Value *, 13> OffloadingArgs = {
8945 SrcLocInfo, DeviceID,
8946 PointerNum, RTArgs.BasePointersArray,
8947 RTArgs.PointersArray, RTArgs.SizesArray,
8948 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8949 RTArgs.MappersArray};
8950
8951 if (IsStandAlone) {
8952 assert(MapperFunc && "MapperFunc missing for standalone target data");
8953
8954 auto TaskBodyCB = [&](Value *, Value *,
8956 if (Info.HasNoWait) {
8957 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
8961 }
8962
8964 OffloadingArgs);
8965
8966 if (Info.HasNoWait) {
8967 BasicBlock *OffloadContBlock =
8968 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
8969 Function *CurFn = Builder.GetInsertBlock()->getParent();
8970 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
8971 Builder.restoreIP(Builder.saveIP());
8972 }
8973 return Error::success();
8974 };
8975
8976 bool RequiresOuterTargetTask = Info.HasNoWait;
8977 if (!RequiresOuterTargetTask)
8978 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
8979 /*TargetTaskAllocaIP=*/{}));
8980 else
8981 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
8982 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
8983 } else {
8984 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
8985 omp::OMPRTL___tgt_target_data_begin_mapper);
8986
8987 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
8988
8989 for (auto DeviceMap : Info.DevicePtrInfoMap) {
8990 if (isa<AllocaInst>(DeviceMap.second.second)) {
8991 auto *LI =
8992 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
8993 Builder.CreateStore(LI, DeviceMap.second.second);
8994 }
8995 }
8996
8997 // If device pointer privatization is required, emit the body of the
8998 // region here. It will have to be duplicated: with and without
8999 // privatization.
9000 InsertPointOrErrorTy AfterIP =
9001 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
9002 if (!AfterIP)
9003 return AfterIP.takeError();
9004 Builder.restoreIP(*AfterIP);
9005 }
9006 return Error::success();
9007 };
9008
9009 // If we need device pointer privatization, we need to emit the body of the
9010 // region with no privatization in the 'else' branch of the conditional.
9011 // Otherwise, we don't have to do anything.
9012 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9013 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9014 InsertPointOrErrorTy AfterIP =
9015 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
9016 if (!AfterIP)
9017 return AfterIP.takeError();
9018 Builder.restoreIP(*AfterIP);
9019 return Error::success();
9020 };
9021
9022 // Generate code for the closing of the data region.
9023 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9024 ArrayRef<BasicBlock *> DeallocBlocks) {
9025 TargetDataRTArgs RTArgs;
9026 Info.EmitDebug = !MapInfo->Names.empty();
9027 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
9028
9029 // Emit the number of elements in the offloading arrays.
9030 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9031
9032 // Source location for the ident struct
9033 if (!SrcLocInfo) {
9034 uint32_t SrcLocStrSize;
9035 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9036 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9037 }
9038
9039 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9040 PointerNum, RTArgs.BasePointersArray,
9041 RTArgs.PointersArray, RTArgs.SizesArray,
9042 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9043 RTArgs.MappersArray};
9044 Function *EndMapperFunc =
9045 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9046
9047 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9048 return Error::success();
9049 };
9050
9051 // We don't have to do anything to close the region if the if clause evaluates
9052 // to false.
9053 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9054 ArrayRef<BasicBlock *> DeallocBlocks) {
9055 return Error::success();
9056 };
9057
9058 Error Err = [&]() -> Error {
9059 if (BodyGenCB) {
9060 Error Err = [&]() {
9061 if (IfCond)
9062 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9063 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9064 }();
9065
9066 if (Err)
9067 return Err;
9068
9069 // If we don't require privatization of device pointers, we emit the body
9070 // in between the runtime calls. This avoids duplicating the body code.
9071 InsertPointOrErrorTy AfterIP =
9072 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9073 if (!AfterIP)
9074 return AfterIP.takeError();
9075 restoreIPandDebugLoc(Builder, *AfterIP);
9076
9077 if (IfCond)
9078 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9079 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9080 }
9081 if (IfCond)
9082 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9083 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9084 }();
9085
9086 if (Err)
9087 return Err;
9088
9089 return Builder.saveIP();
9090}
9091
9094 bool IsGPUDistribute) {
9095 assert((IVSize == 32 || IVSize == 64) &&
9096 "IV size is not compatible with the omp runtime");
9097 RuntimeFunction Name;
9098 if (IsGPUDistribute)
9099 Name = IVSize == 32
9100 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9101 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9102 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9103 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9104 else
9105 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9106 : omp::OMPRTL___kmpc_for_static_init_4u)
9107 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9108 : omp::OMPRTL___kmpc_for_static_init_8u);
9109
9110 return getOrCreateRuntimeFunction(M, Name);
9111}
9112
9114 bool IVSigned) {
9115 assert((IVSize == 32 || IVSize == 64) &&
9116 "IV size is not compatible with the omp runtime");
9117 RuntimeFunction Name = IVSize == 32
9118 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9119 : omp::OMPRTL___kmpc_dispatch_init_4u)
9120 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9121 : omp::OMPRTL___kmpc_dispatch_init_8u);
9122
9123 return getOrCreateRuntimeFunction(M, Name);
9124}
9125
9127 bool IVSigned) {
9128 assert((IVSize == 32 || IVSize == 64) &&
9129 "IV size is not compatible with the omp runtime");
9130 RuntimeFunction Name = IVSize == 32
9131 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9132 : omp::OMPRTL___kmpc_dispatch_next_4u)
9133 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9134 : omp::OMPRTL___kmpc_dispatch_next_8u);
9135
9136 return getOrCreateRuntimeFunction(M, Name);
9137}
9138
9140 bool IVSigned) {
9141 assert((IVSize == 32 || IVSize == 64) &&
9142 "IV size is not compatible with the omp runtime");
9143 RuntimeFunction Name = IVSize == 32
9144 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9145 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9146 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9147 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9148
9149 return getOrCreateRuntimeFunction(M, Name);
9150}
9151
9153 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9154}
9155
9157 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9158 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9159
9160 DISubprogram *NewSP = Func->getSubprogram();
9161 if (!NewSP)
9162 return;
9163
9165
9166 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9167 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9168 // Only use cached variable if the arg number matches. This is important
9169 // so that DIVariable created for privatized variables are not discarded.
9170 if (NewVar && (arg == NewVar->getArg()))
9171 return NewVar;
9172
9174 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9175 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9176 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9177 return NewVar;
9178 };
9179
9180 auto UpdateDebugRecord = [&](auto *DR) {
9181 DILocalVariable *OldVar = DR->getVariable();
9182 unsigned ArgNo = 0;
9183 for (auto Loc : DR->location_ops()) {
9184 auto Iter = ValueReplacementMap.find(Loc);
9185 if (Iter != ValueReplacementMap.end()) {
9186 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9187 ArgNo = std::get<1>(Iter->second) + 1;
9188 }
9189 }
9190 if (ArgNo != 0)
9191 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9192 };
9193
9195 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9196 if (DVR->getNumVariableLocationOps() != 1u) {
9197 DVR->setKillLocation();
9198 return;
9199 }
9200 Value *Loc = DVR->getVariableLocationOp(0u);
9201 BasicBlock *CurBB = DVR->getParent();
9202 BasicBlock *RequiredBB = nullptr;
9203
9204 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9205 RequiredBB = LocInst->getParent();
9206 else if (isa<llvm::Argument>(Loc))
9207 RequiredBB = &DVR->getFunction()->getEntryBlock();
9208
9209 if (RequiredBB && RequiredBB != CurBB) {
9210 assert(!RequiredBB->empty());
9211 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9212 RequiredBB->back().getIterator());
9213 DVRsToDelete.push_back(DVR);
9214 }
9215 };
9216
9217 // The location and scope of variable intrinsics and records still point to
9218 // the parent function of the target region. Update them.
9219 for (Instruction &I : instructions(Func)) {
9221 "Unexpected debug intrinsic");
9222 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9223 UpdateDebugRecord(&DVR);
9224 MoveDebugRecordToCorrectBlock(&DVR);
9225 }
9226 }
9227 for (auto *DVR : DVRsToDelete)
9228 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9229 // An extra argument is passed to the device. Create the debug data for it.
9230 if (OMPBuilder.Config.isTargetDevice()) {
9231 DICompileUnit *CU = NewSP->getUnit();
9232 Module *M = Func->getParent();
9233 DIBuilder DB(*M, true, CU);
9234 DIType *VoidPtrTy =
9235 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9236 unsigned ArgNo = Func->arg_size();
9237 DILocalVariable *Var = DB.createParameterVariable(
9238 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9239 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9240 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9241 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9242 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9243 &(*Func->begin()));
9244 }
9245}
9246
9248 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9249 return cast<Operator>(V)->getOperand(0);
9250 return V;
9251}
9252
9254 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9256 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9259 DebugLoc OutlinedFnLoc) {
9260 SmallVector<Type *> ParameterTypes;
9261 if (OMPBuilder.Config.isTargetDevice()) {
9262 // All parameters to target devices are passed as pointers
9263 // or i64. This assumes 64-bit address spaces/pointers.
9264 for (auto &Arg : Inputs)
9265 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9266 ? Arg->getType()
9267 : Type::getInt64Ty(Builder.getContext()));
9268 } else {
9269 for (auto &Arg : Inputs)
9270 ParameterTypes.push_back(Arg->getType());
9271 }
9272
9273 // The implicit dyn_ptr argument is always the last parameter on both host
9274 // and device so the argument counts match without runtime manipulation.
9275 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9276 ParameterTypes.push_back(PtrTy);
9277
9278 auto BB = Builder.GetInsertBlock();
9279 auto M = BB->getModule();
9280 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9281 /*isVarArg*/ false);
9282 auto Func =
9283 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9284
9285 // Forward target-cpu and target-features function attributes from the
9286 // original function to the new outlined function.
9287 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9288
9289 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9290 if (TargetCpuAttr.isStringAttribute())
9291 Func->addFnAttr(TargetCpuAttr);
9292
9293 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9294 if (TargetFeaturesAttr.isStringAttribute())
9295 Func->addFnAttr(TargetFeaturesAttr);
9296
9297 if (OMPBuilder.Config.isTargetDevice()) {
9298 Value *ExecMode =
9299 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9300 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9301 }
9302
9303 // Save insert point.
9304 IRBuilder<>::InsertPointGuard IPG(Builder);
9305 // We will generate the entries in the outlined function but the debug
9306 // location is still pointing to the parent function, which is the wrong
9307 // scope. OutlinedFnLoc, when the caller provides one, is the same source
9308 // position scoped to the subprogram that will be attached to the outlined
9309 // function, so it is what everything emitted below needs.
9310 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9311
9312 // Generate the region into the function.
9313 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9314 Builder.SetInsertPoint(EntryBB);
9315
9316 // Insert target init call in the device compilation pass.
9317 if (OMPBuilder.Config.isTargetDevice())
9318 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9319
9320 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9321
9322 // As we embed the user code in the middle of our target region after we
9323 // generate entry code, we must move what allocas we can into the entry
9324 // block to avoid possible breaking optimisations for device
9325 if (OMPBuilder.Config.isTargetDevice())
9327
9328 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9329 BasicBlock *OutlinedBodyBB =
9330 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9332 Builder.saveIP(),
9333 OpenMPIRBuilder::InsertPointTy(OutlinedBodyBB, OutlinedBodyBB->begin()),
9334 ExitBB);
9335 if (!AfterIP)
9336 return AfterIP.takeError();
9337 Builder.SetInsertPoint(ExitBB);
9338 // The body callback builds the body with its own IRBuilder and cannot reach
9339 // this one directly. But a body holding another OpenMP construct, a nested
9340 // parallel say, calls OpenMPIRBuilder::createParallel, and that can leave
9341 // this Builder pointing at the wrong debug location, or at none at all. The
9342 // epilogue below belongs to the target construct rather than to whatever the
9343 // body emitted last, so re-establish the location the prologue was emitted
9344 // with.
9345 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9346
9347 // Insert target deinit call in the device compilation pass.
9348 if (OMPBuilder.Config.isTargetDevice())
9349 OMPBuilder.createTargetDeinit(Builder);
9350
9351 // Insert return instruction.
9352 Builder.CreateRetVoid();
9353
9354 // New Alloca IP at entry point of created device function.
9355 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9356 auto AllocaIP = Builder.saveIP();
9357
9358 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());
9359
9360 // Do not include the artificial dyn_ptr argument.
9361 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9362
9364
9365 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9366 // Things like GEP's can come in the form of Constants. Constants and
9367 // ConstantExpr's do not have access to the knowledge of what they're
9368 // contained in, so we must dig a little to find an instruction so we
9369 // can tell if they're used inside of the function we're outlining. We
9370 // also replace the original constant expression with a new instruction
9371 // equivalent; an instruction as it allows easy modification in the
9372 // following loop, as we can now know the constant (instruction) is
9373 // owned by our target function and replaceUsesOfWith can now be invoked
9374 // on it (cannot do this with constants it seems). A brand new one also
9375 // allows us to be cautious as it is perhaps possible the old expression
9376 // was used inside of the function but exists and is used externally
9377 // (unlikely by the nature of a Constant, but still).
9378 // NOTE: We cannot remove dead constants that have been rewritten to
9379 // instructions at this stage, we run the risk of breaking later lowering
9380 // by doing so as we could still be in the process of lowering the module
9381 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9382 // constants we have created rewritten versions of.
9383 if (auto *Const = dyn_cast<Constant>(Input))
9384 convertUsersOfConstantsToInstructions(Const, Func, false);
9385
9386 // Collect users before iterating over them to avoid invalidating the
9387 // iteration in case a user uses Input more than once (e.g. a call
9388 // instruction).
9389 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9390 // Collect all the instructions
9392 if (auto *Instr = dyn_cast<Instruction>(User))
9393 if (Instr->getFunction() == Func)
9394 Instr->replaceUsesOfWith(Input, InputCopy);
9395 };
9396
9397 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9398
9399 // Rewrite uses of input valus to parameters.
9400 for (auto InArg : zip(Inputs, ArgRange)) {
9401 Value *Input = std::get<0>(InArg);
9402 Argument &Arg = std::get<1>(InArg);
9403 Value *InputCopy = nullptr;
9404
9405 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9406 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(),
9407 OpenMPIRBuilder::InsertPointTy(ExitBB, ExitBB->begin()));
9408 if (!AfterIP)
9409 return AfterIP.takeError();
9410 Builder.restoreIP(*AfterIP);
9411 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9412
9413 // In certain cases a Global may be set up for replacement, however, this
9414 // Global may be used in multiple arguments to the kernel, just segmented
9415 // apart, for example, if we have a global array, that is sectioned into
9416 // multiple mappings (technically not legal in OpenMP, but there is a case
9417 // in Fortran for Common Blocks where this is neccesary), we will end up
9418 // with GEP's into this array inside the kernel, that refer to the Global
9419 // but are technically separate arguments to the kernel for all intents and
9420 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9421 // index, it will fold into an referal to the Global, if we then encounter
9422 // this folded GEP during replacement all of the references to the
9423 // Global in the kernel will be replaced with the argument we have generated
9424 // that corresponds to it, including any other GEP's that refer to the
9425 // Global that may be other arguments. This will invalidate all of the other
9426 // preceding mapped arguments that refer to the same global that may be
9427 // separate segments. To prevent this, we defer global processing until all
9428 // other processing has been performed.
9431 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9432 continue;
9433 }
9434
9436 continue;
9437
9438 ReplaceValue(Input, InputCopy, Func);
9439 }
9440
9441 // Replace all of our deferred Input values, currently just Globals.
9442 for (auto Deferred : DeferredReplacement)
9443 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9444
9445 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9446 ValueReplacementMap);
9447 return Func;
9448}
9449/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9450/// of pointers containing shared data between the parent task and the created
9451/// task.
9453 IRBuilderBase &Builder,
9454 Value *TaskWithPrivates,
9455 Type *TaskWithPrivatesTy) {
9456
9457 Type *TaskTy = OMPIRBuilder.Task;
9458 LLVMContext &Ctx = Builder.getContext();
9459 Value *TaskT =
9460 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9461 Value *Shareds = TaskT;
9462 // TaskWithPrivatesTy can be one of the following
9463 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9464 // %struct.privates }
9465 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9466 //
9467 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9468 // its first member has to be the task descriptor. TaskTy is the type of the
9469 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9470 // first member of TaskT, gives us the pointer to shared data.
9471 if (TaskWithPrivatesTy != TaskTy)
9472 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9473 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9474}
9475/// Create an entry point for a target task with the following.
9476/// It'll have the following signature
9477/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9478/// This function is called from emitTargetTask once the
9479/// code to launch the target kernel has been outlined already.
9480/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9481/// into the task structure so that the deferred target task can access this
9482/// data even after the stack frame of the generating task has been rolled
9483/// back. Offloading arrays contain base pointers, pointers, sizes etc
9484/// of the data that the target kernel will access. These in effect are the
9485/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9487 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9488 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9489 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9490
9491 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9492 // This is because PrivatesTy is the type of the structure in which
9493 // we pass the offloading arrays to the deferred target task.
9494 assert((!NumOffloadingArrays || PrivatesTy) &&
9495 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9496 "to privatize");
9497
9498 Module &M = OMPBuilder.M;
9499 // KernelLaunchFunction is the target launch function, i.e.
9500 // the function that sets up kernel arguments and calls
9501 // __tgt_target_kernel to launch the kernel on the device.
9502 //
9503 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9504
9505 // StaleCI is the CallInst which is the call to the outlined
9506 // target kernel launch function. If there are local live-in values
9507 // that the outlined function uses then these are aggregated into a structure
9508 // which is passed as the second argument. If there are no local live-in
9509 // values or if all values used by the outlined kernel are global variables,
9510 // then there's only one argument, the threadID. So, StaleCI can be
9511 //
9512 // %structArg = alloca { ptr, ptr }, align 8
9513 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9514 // store ptr %20, ptr %gep_, align 8
9515 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9516 // store ptr %21, ptr %gep_8, align 8
9517 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9518 //
9519 // OR
9520 //
9521 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9523 StaleCI->getIterator());
9524
9525 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9526
9527 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9528 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9529 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9530
9531 auto ProxyFnTy =
9532 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9533 /* isVarArg */ false);
9534 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9535 ".omp_target_task_proxy_func", M);
9536 Value *ThreadId = ProxyFn->getArg(0);
9537 Value *TaskWithPrivates = ProxyFn->getArg(1);
9538 ThreadId->setName("thread.id");
9539 TaskWithPrivates->setName("task");
9540
9541 bool HasShareds = SharedArgsOperandNo > 0;
9542 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9543 IRBuilder<>::InsertPointGuard IPG(Builder);
9544 BasicBlock *EntryBB =
9545 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9546 Builder.SetInsertPoint(EntryBB);
9547 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9548
9549 SmallVector<Value *> KernelLaunchArgs;
9550 KernelLaunchArgs.reserve(StaleCI->arg_size());
9551 KernelLaunchArgs.push_back(ThreadId);
9552
9553 if (HasOffloadingArrays) {
9554 assert(TaskTy != TaskWithPrivatesTy &&
9555 "If there are offloading arrays to pass to the target"
9556 "TaskTy cannot be the same as TaskWithPrivatesTy");
9557 (void)TaskTy;
9558 Value *Privates =
9559 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9560 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9561 KernelLaunchArgs.push_back(
9562 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9563 }
9564
9565 if (HasShareds) {
9566 auto *ArgStructAlloca =
9567 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9568 assert(ArgStructAlloca &&
9569 "Unable to find the alloca instruction corresponding to arguments "
9570 "for extracted function");
9571 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9572 std::optional<TypeSize> ArgAllocSize =
9573 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9574 assert(ArgStructType && ArgAllocSize &&
9575 "Unable to determine size of arguments for extracted function");
9576 uint64_t StructSize = ArgAllocSize->getFixedValue();
9577
9578 AllocaInst *NewArgStructAlloca =
9579 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9580
9581 Value *SharedsSize = Builder.getInt64(StructSize);
9582
9584 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9585
9586 Builder.CreateMemCpy(
9587 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9588 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9589 KernelLaunchArgs.push_back(NewArgStructAlloca);
9590 }
9591 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9592 Builder.CreateRetVoid();
9593 return ProxyFn;
9594}
9596
9597 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9598 return GEP->getSourceElementType();
9599 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9600 return Alloca->getAllocatedType();
9601
9602 llvm_unreachable("Unhandled Instruction type");
9603 return nullptr;
9604}
9605// This function returns a struct that has at most two members.
9606// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9607// descriptor. The second member, if needed, is a struct containing arrays
9608// that need to be passed to the offloaded target kernel. For example,
9609// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9610// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9611// respectively, then the types created by this function are
9612//
9613// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9614// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9615// %struct.privates }
9616// %struct.task_with_privates is returned by this function.
9617// If there aren't any offloading arrays to pass to the target kernel,
9618// %struct.kmp_task_ompbuilder_t is returned.
9619static StructType *
9621 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9622
9623 if (OffloadingArraysToPrivatize.empty())
9624 return OMPIRBuilder.Task;
9625
9626 SmallVector<Type *, 4> StructFieldTypes;
9627 for (Value *V : OffloadingArraysToPrivatize) {
9628 assert(V->getType()->isPointerTy() &&
9629 "Expected pointer to array to privatize. Got a non-pointer value "
9630 "instead");
9631 Type *ArrayTy = getOffloadingArrayType(V);
9632 assert(ArrayTy && "ArrayType cannot be nullptr");
9633 StructFieldTypes.push_back(ArrayTy);
9634 }
9635 StructType *PrivatesStructTy =
9636 StructType::create(StructFieldTypes, "struct.privates");
9637 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9638 "struct.task_with_privates");
9639}
9641 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9642 TargetRegionEntryInfo &EntryInfo,
9644 Function *&OutlinedFn, Constant *&OutlinedFnID,
9648 DebugLoc OutlinedFnLoc) {
9649
9650 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9651 [&](StringRef EntryFnName) {
9652 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9653 EntryFnName, Inputs, CBFunc,
9654 ArgAccessorFuncCB, OutlinedFnLoc);
9655 };
9656
9657 return OMPBuilder.emitTargetRegionFunction(
9658 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9659 OutlinedFnID);
9660}
9661
9663 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9665 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9666 bool HasNoWait) {
9667
9668 // The following explains the code-gen scenario for the `target` directive. A
9669 // similar scneario is followed for other device-related directives (e.g.
9670 // `target enter data`) but in similar fashion since we only need to emit task
9671 // that encapsulates the proper runtime call.
9672 //
9673 // When we arrive at this function, the target region itself has been
9674 // outlined into the function OutlinedFn.
9675 // So at ths point, for
9676 // --------------------------------------------------------------
9677 // void user_code_that_offloads(...) {
9678 // omp target depend(..) map(from:a) map(to:b) private(i)
9679 // do i = 1, 10
9680 // a(i) = b(i) + n
9681 // }
9682 //
9683 // --------------------------------------------------------------
9684 //
9685 // we have
9686 //
9687 // --------------------------------------------------------------
9688 //
9689 // void user_code_that_offloads(...) {
9690 // %.offload_baseptrs = alloca [2 x ptr], align 8
9691 // %.offload_ptrs = alloca [2 x ptr], align 8
9692 // %.offload_mappers = alloca [2 x ptr], align 8
9693 // ;; target region has been outlined and now we need to
9694 // ;; offload to it via a target task.
9695 // }
9696 // void outlined_device_function(ptr a, ptr b, ptr n) {
9697 // n = *n_ptr;
9698 // do i = 1, 10
9699 // a(i) = b(i) + n
9700 // }
9701 //
9702 // We have to now do the following
9703 // (i) Make an offloading call to outlined_device_function using the OpenMP
9704 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9705 // emitted by emitKernelLaunch
9706 // (ii) Create a task entry point function that calls kernel_launch_function
9707 // and is the entry point for the target task. See
9708 // '@.omp_target_task_proxy_func in the pseudocode below.
9709 // (iii) Create a task with the task entry point created in (ii)
9710 //
9711 // That is we create the following
9712 // struct task_with_privates {
9713 // struct kmp_task_ompbuilder_t task_struct;
9714 // struct privates {
9715 // [2 x ptr] ; baseptrs
9716 // [2 x ptr] ; ptrs
9717 // [2 x i64] ; sizes
9718 // }
9719 // }
9720 // void user_code_that_offloads(...) {
9721 // %.offload_baseptrs = alloca [2 x ptr], align 8
9722 // %.offload_ptrs = alloca [2 x ptr], align 8
9723 // %.offload_sizes = alloca [2 x i64], align 8
9724 //
9725 // %structArg = alloca { ptr, ptr, ptr }, align 8
9726 // %strucArg[0] = a
9727 // %strucArg[1] = b
9728 // %strucArg[2] = &n
9729 //
9730 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9731 // sizeof(kmp_task_ompbuilder_t),
9732 // sizeof(structArg),
9733 // @.omp_target_task_proxy_func,
9734 // ...)
9735 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9736 // sizeof(structArg))
9737 // memcpy(target_task_with_privates->privates->baseptrs,
9738 // offload_baseptrs, sizeof(offload_baseptrs)
9739 // memcpy(target_task_with_privates->privates->ptrs,
9740 // offload_ptrs, sizeof(offload_ptrs)
9741 // memcpy(target_task_with_privates->privates->sizes,
9742 // offload_sizes, sizeof(offload_sizes)
9743 // dependencies_array = ...
9744 // ;; if nowait not present
9745 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9746 // call @__kmpc_omp_task_begin_if0(...)
9747 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9748 // %target_task_with_privates)
9749 // call @__kmpc_omp_task_complete_if0(...)
9750 // }
9751 //
9752 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9753 // ptr %task) {
9754 // %structArg = alloca {ptr, ptr, ptr}
9755 // %task_ptr = getelementptr(%task, 0, 0)
9756 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9757 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9758 //
9759 // %offloading_arrays = getelementptr(%task, 0, 1)
9760 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9761 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9762 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9763 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9764 // %offload_sizes, %structArg)
9765 // }
9766 //
9767 // We need the proxy function because the signature of the task entry point
9768 // expected by kmpc_omp_task is always the same and will be different from
9769 // that of the kernel_launch function.
9770 //
9771 // kernel_launch_function is generated by emitKernelLaunch and has the
9772 // always_inline attribute. For this example, it'll look like so:
9773 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9774 // %offload_sizes, %structArg) alwaysinline {
9775 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9776 // ; load aggregated data from %structArg
9777 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9778 // ; offload_sizes
9779 // call i32 @__tgt_target_kernel(...,
9780 // outlined_device_function,
9781 // ptr %kernel_args)
9782 // }
9783 // void outlined_device_function(ptr a, ptr b, ptr n) {
9784 // n = *n_ptr;
9785 // do i = 1, 10
9786 // a(i) = b(i) + n
9787 // }
9788 //
9789 BasicBlock *TargetTaskBodyBB =
9790 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9791 BasicBlock *TargetTaskAllocaBB =
9792 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9793
9794 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB,
9795 TargetTaskAllocaBB->begin());
9796 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB, TargetTaskBodyBB->begin());
9797
9798 auto OI = std::make_unique<OutlineInfo>();
9799 OI->EntryBB = TargetTaskAllocaBB;
9800 OI->OuterAllocBB = AllocaIP.getBlock();
9801
9802 // Add the thread ID argument.
9804 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9805 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9806
9807 // Generate the task body which will subsequently be outlined.
9808 Builder.restoreIP(TargetTaskBodyIP);
9809 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9810 return Err;
9811
9812 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9813 // it is given. These blocks are enumerated by
9814 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9815 // to be outside the region. In other words, OI.ExitBlock is expected to be
9816 // the start of the region after the outlining. We used to set OI.ExitBlock
9817 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9818 // except when the task body is a single basic block. In that case,
9819 // OI.ExitBlock is set to the single task body block and will get left out of
9820 // the outlining process. So, simply create a new empty block to which we
9821 // uncoditionally branch from where TaskBodyCB left off
9822 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9823 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9824 /*IsFinished=*/true);
9825
9826 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9827 bool NeedsTargetTask = HasNoWait && DeviceID;
9828 if (NeedsTargetTask) {
9829 for (auto *V :
9830 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9831 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9832 RTArgs.SizesArray}) {
9834 OffloadingArraysToPrivatize.push_back(V);
9835 OI->ExcludeArgsFromAggregate.push_back(V);
9836 }
9837 }
9838 }
9839 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9840 DeviceID, OffloadingArraysToPrivatize](
9841 Function &OutlinedFn) mutable {
9842 assert(OutlinedFn.hasOneUse() &&
9843 "there must be a single user for the outlined function");
9844
9845 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9846
9847 // The first argument of StaleCI is always the thread id.
9848 // The next few arguments are the pointers to offloading arrays
9849 // if any. (see OffloadingArraysToPrivatize)
9850 // Finally, all other local values that are live-in into the outlined region
9851 // end up in a structure whose pointer is passed as the last argument. This
9852 // piece of data is passed in the "shared" field of the task structure. So,
9853 // we know we have to pass shareds to the task if the number of arguments is
9854 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9855 // thread id. Further, for safety, we assert that the number of arguments of
9856 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9857 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9858 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9859 assert((!HasShareds ||
9860 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9861 "Wrong number of arguments for StaleCI when shareds are present");
9862 int SharedArgOperandNo =
9863 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9864
9865 StructType *TaskWithPrivatesTy =
9866 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9867 StructType *PrivatesTy = nullptr;
9868
9869 if (!OffloadingArraysToPrivatize.empty())
9870 PrivatesTy =
9871 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9872
9874 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9875 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9876
9877 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9878 << "\n");
9879
9880 Builder.SetInsertPoint(StaleCI);
9881
9882 // Gather the arguments for emitting the runtime call.
9883 uint32_t SrcLocStrSize;
9884 Constant *SrcLocStr =
9886 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9887
9888 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9889 //
9890 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9891 // the DeviceID to the deferred task and also since
9892 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9893 Function *TaskAllocFn =
9894 !NeedsTargetTask
9895 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9897 OMPRTL___kmpc_omp_target_task_alloc);
9898
9899 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9900 // call.
9901 Value *ThreadID = getOrCreateThreadID(Ident);
9902
9903 // Argument - `sizeof_kmp_task_t` (TaskSize)
9904 // Tasksize refers to the size in bytes of kmp_task_t data structure
9905 // plus any other data to be passed to the target task, if any, which
9906 // is packed into a struct. kmp_task_t and the struct so created are
9907 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9908 Value *TaskSize = Builder.getInt64(
9909 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9910
9911 // Argument - `sizeof_shareds` (SharedsSize)
9912 // SharedsSize refers to the shareds array size in the kmp_task_t data
9913 // structure.
9914 Value *SharedsSize = Builder.getInt64(0);
9915 if (HasShareds) {
9916 auto *ArgStructAlloca =
9917 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9918 assert(ArgStructAlloca &&
9919 "Unable to find the alloca instruction corresponding to arguments "
9920 "for extracted function");
9921 std::optional<TypeSize> ArgAllocSize =
9922 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9923 assert(ArgAllocSize &&
9924 "Unable to determine size of arguments for extracted function");
9925 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9926 }
9927
9928 // Argument - `flags`
9929 // Task is tied iff (Flags & 1) == 1.
9930 // Task is untied iff (Flags & 1) == 0.
9931 // Task is final iff (Flags & 2) == 2.
9932 // Task is not final iff (Flags & 2) == 0.
9933 // A target task is not final and is untied.
9934 Value *Flags = Builder.getInt32(0);
9935
9936 // Emit the @__kmpc_omp_task_alloc runtime call
9937 // The runtime call returns a pointer to an area where the task captured
9938 // variables must be copied before the task is run (TaskData)
9939 CallInst *TaskData = nullptr;
9940
9941 SmallVector<llvm::Value *> TaskAllocArgs = {
9942 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
9943 /*flags=*/Flags,
9944 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
9945 /*task_func=*/ProxyFn};
9946
9947 if (NeedsTargetTask) {
9948 assert(DeviceID && "Expected non-empty device ID.");
9949 TaskAllocArgs.push_back(DeviceID);
9950 }
9951
9952 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
9953
9954 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
9955 if (HasShareds) {
9956 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
9958 *this, Builder, TaskData, TaskWithPrivatesTy);
9959 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
9960 SharedsSize);
9961 }
9962 if (!OffloadingArraysToPrivatize.empty()) {
9963 Value *Privates =
9964 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
9965 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
9966 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
9967 [[maybe_unused]] Type *ArrayType =
9968 getOffloadingArrayType(PtrToPrivatize);
9969 assert(ArrayType && "ArrayType cannot be nullptr");
9970
9971 Type *ElementType = PrivatesTy->getElementType(i);
9972 assert(ElementType == ArrayType &&
9973 "ElementType should match ArrayType");
9974 (void)ArrayType;
9975
9976 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
9977 Builder.CreateMemCpy(
9978 Dst, Alignment, PtrToPrivatize, Alignment,
9979 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
9980 }
9981 }
9982
9983 Value *DepArray = nullptr;
9984 Value *NumDeps = nullptr;
9985 if (Dependencies.DepArray) {
9986 DepArray = Dependencies.DepArray;
9987 NumDeps = Dependencies.NumDeps;
9988 } else if (!Dependencies.Deps.empty()) {
9989 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
9990 NumDeps = Builder.getInt32(Dependencies.Deps.size());
9991 }
9992
9993 // ---------------------------------------------------------------
9994 // V5.2 13.8 target construct
9995 // If the nowait clause is present, execution of the target task
9996 // may be deferred. If the nowait clause is not present, the target task is
9997 // an included task.
9998 // ---------------------------------------------------------------
9999 // The above means that the lack of a nowait on the target construct
10000 // translates to '#pragma omp task if(0)'
10001 if (!NeedsTargetTask) {
10002 if (DepArray) {
10003 Function *TaskWaitFn =
10004 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
10006 TaskWaitFn,
10007 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
10008 /*ndeps=*/NumDeps,
10009 /*dep_list=*/DepArray,
10010 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
10011 /*noalias_dep_list=*/
10013 }
10014 // Included task.
10015 Function *TaskBeginFn =
10016 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
10017 Function *TaskCompleteFn =
10018 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
10019 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
10020 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
10021 CI->setDebugLoc(StaleCI->getDebugLoc());
10022 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
10023 } else if (DepArray) {
10024 // HasNoWait - meaning the task may be deferred. Call
10025 // __kmpc_omp_task_with_deps if there are dependencies,
10026 // else call __kmpc_omp_task
10027 Function *TaskFn =
10028 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
10030 TaskFn,
10031 {Ident, ThreadID, TaskData, NumDeps, DepArray,
10032 ConstantInt::get(Builder.getInt32Ty(), 0),
10034 } else {
10035 // Emit the @__kmpc_omp_task runtime call to spawn the task
10036 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
10037 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
10038 }
10039
10040 Builder.ClearInsertionPoint();
10041 StaleCI->eraseFromParent();
10042 for (Instruction *I : llvm::reverse(ToBeDeleted))
10043 I->eraseFromParent();
10044 };
10045 addOutlineInfo(std::move(OI));
10046
10047 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
10048 << *(Builder.GetInsertBlock()) << "\n");
10049 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
10050 << *(Builder.GetInsertBlock()->getParent()->getParent())
10051 << "\n");
10052 return Builder.saveIP();
10053}
10054
10056 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10057 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10058 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10059 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10060 if (Error Err =
10061 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10062 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10063 return Err;
10064 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10065 return Error::success();
10066}
10067
10068static void emitTargetCall(
10069 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
10074 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10078 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10079 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10080 // Generate a function call to the host fallback implementation of the target
10081 // region. This is called by the host when no offload entry was generated for
10082 // the target region and when the offloading call fails at runtime.
10083 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10085 Builder.restoreIP(IP);
10086 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10087 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10088 FallbackArgs.push_back(
10089 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10090 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10091 return Builder.saveIP();
10092 };
10093
10094 bool HasDependencies = !Dependencies.empty();
10095 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10096
10098
10099 auto TaskBodyCB =
10100 [&](Value *DeviceID, Value *RTLoc,
10101 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10102 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10103 // produce any.
10105 // emitKernelLaunch makes the necessary runtime call to offload the
10106 // kernel. We then outline all that code into a separate function
10107 // ('kernel_launch_function' in the pseudo code above). This function is
10108 // then called by the target task proxy function (see
10109 // '@.omp_target_task_proxy_func' in the pseudo code above)
10110 // "@.omp_target_task_proxy_func' is generated by
10111 // emitTargetTaskProxyFunction.
10112 if (OutlinedFnID && DeviceID)
10113 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10114 EmitTargetCallFallbackCB, KArgs,
10115 DeviceID, RTLoc, TargetTaskAllocaIP);
10116
10117 // We only need to do the outlining if `DeviceID` is set to avoid calling
10118 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10119 // generating the `else` branch of an `if` clause.
10120 //
10121 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10122 // In this case, we execute the host implementation directly.
10123 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10124 }());
10125
10126 OMPBuilder.Builder.restoreIP(AfterIP);
10127 return Error::success();
10128 };
10129
10130 auto &&EmitTargetCallElse =
10131 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10133 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10134 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10135 // produce any.
10137 if (RequiresOuterTargetTask) {
10138 // Arguments that are intended to be directly forwarded to an
10139 // emitKernelLaunch call are pased as nullptr, since
10140 // OutlinedFnID=nullptr results in that call not being done.
10142 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10143 /*RTLoc=*/nullptr, AllocaIP,
10144 Dependencies, EmptyRTArgs, HasNoWait);
10145 }
10146 return EmitTargetCallFallbackCB(Builder.saveIP());
10147 }());
10148
10149 Builder.restoreIP(AfterIP);
10150 return Error::success();
10151 };
10152
10153 auto &&EmitTargetCallThen =
10154 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10156 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10157 Info.HasNoWait = HasNoWait;
10158 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10159
10161 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10162 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10163 /*IsNonContiguous=*/true,
10164 /*ForEndCall=*/false))
10165 return Err;
10166
10167 SmallVector<Value *, 3> NumTeamsC;
10168 for (auto [DefaultVal, RuntimeVal] :
10169 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10170 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10171 : Builder.getInt32(DefaultVal));
10172
10173 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10174 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10175 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10176 if (Clause)
10177 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10178 /*isSigned=*/false);
10179 return Clause;
10180 };
10181 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10182 if (Clause)
10183 Result =
10184 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10185 Result, Clause)
10186 : Clause;
10187 };
10188
10189 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10190 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10191 SmallVector<Value *, 3> NumThreadsC;
10192 Value *MaxThreadsClause =
10193 RuntimeAttrs.TeamsThreadLimit.size() == 1
10194 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads.front())
10195 : nullptr;
10196
10197 for (auto [TeamsVal, TargetVal] : zip_equal(
10198 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10199 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10200 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10201
10202 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10203 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10204
10205 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10206 }
10207
10208 unsigned NumTargetItems = Info.NumberOfPtrs;
10209 uint32_t SrcLocStrSize;
10210 Constant *SrcLocStr = OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10211 Value *RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10212 llvm::omp::IdentFlag(0), 0);
10213
10214 Value *TripCount = RuntimeAttrs.LoopTripCount
10215 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10216 Builder.getInt64Ty(),
10217 /*isSigned=*/false)
10218 : Builder.getInt64(0);
10219
10220 // Request zero groupprivate bytes by default.
10221 if (!DynCGroupMem)
10222 DynCGroupMem = Builder.getInt32(0);
10223
10225 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10226 HasNoWait, /*StrictBlocks=*/false, /*StrictThreads=*/false,
10227 DynCGroupMemFallback);
10228
10229 // Assume no error was returned because TaskBodyCB and
10230 // EmitTargetCallFallbackCB don't produce any.
10232 // The presence of certain clauses on the target directive require the
10233 // explicit generation of the target task.
10234 if (RequiresOuterTargetTask)
10235 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10236 RTLoc, AllocaIP, Dependencies,
10237 KArgs.RTArgs, Info.HasNoWait);
10238
10239 return OMPBuilder.emitKernelLaunch(
10240 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10241 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10242 }());
10243
10244 Builder.restoreIP(AfterIP);
10245 return Error::success();
10246 };
10247
10248 // If we don't have an ID for the target region, it means an offload entry
10249 // wasn't created. In this case we just run the host fallback directly and
10250 // ignore any potential 'if' clauses.
10251 if (!OutlinedFnID) {
10252 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10253 return;
10254 }
10255
10256 // If there's no 'if' clause, only generate the kernel launch code path.
10257 if (!IfCond) {
10258 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10259 return;
10260 }
10261
10262 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10263 EmitTargetCallElse, AllocaIP));
10264}
10265
10267 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10268 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10269 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10270 const TargetKernelDefaultAttrs &DefaultAttrs,
10271 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10272 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10275 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10276 bool HasNowait, Value *DynCGroupMem,
10277 OMPDynGroupprivateFallbackType DynCGroupMemFallback,
10278 DebugLoc OutlinedFnLoc) {
10279
10280 if (!updateToLocation(Loc))
10281 return InsertPointTy();
10282
10283 Builder.restoreIP(CodeGenIP);
10284
10285 Function *OutlinedFn;
10286 Constant *OutlinedFnID = nullptr;
10287 // The target region is outlined into its own function. The LLVM IR for
10288 // the target region itself is generated using the callbacks CBFunc
10289 // and ArgAccessorFuncCB
10291 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10292 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB, OutlinedFnLoc))
10293 return Err;
10294
10295 // If we are not on the target device, then we need to generate code
10296 // to make a remote call (offload) to the previously outlined function
10297 // that represents the target region. Do that now.
10298 if (!Config.isTargetDevice())
10299 emitTargetCall(*this, Builder, AllocaIP, DeallocBlocks, Info, DefaultAttrs,
10300 RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID, Inputs,
10301 GenMapInfoCB, CustomMapperCB, Dependencies, HasNowait,
10302 DynCGroupMem, DynCGroupMemFallback);
10303 return Builder.saveIP();
10304}
10305
10306std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10307 StringRef FirstSeparator,
10308 StringRef Separator) {
10309 SmallString<128> Buffer;
10310 llvm::raw_svector_ostream OS(Buffer);
10311 StringRef Sep = FirstSeparator;
10312 for (StringRef Part : Parts) {
10313 OS << Sep << Part;
10314 Sep = Separator;
10315 }
10316 return OS.str().str();
10317}
10318
10319std::string
10321 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10322 Config.separator());
10323}
10324
10326 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10327 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10328 if (Elem.second) {
10329 assert(Elem.second->getValueType() == Ty &&
10330 "OMP internal variable has different type than requested");
10331 } else {
10332 // TODO: investigate the appropriate linkage type used for the global
10333 // variable for possibly changing that to internal or private, or maybe
10334 // create different versions of the function for different OMP internal
10335 // variables.
10336 const DataLayout &DL = M.getDataLayout();
10337 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10338 // default global AS is 1.
10339 // See double-target-call-with-declare-target.f90 and
10340 // declare-target-vars-in-target-region.f90 libomptarget
10341 // tests.
10342 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10343 : M.getTargetTriple().isAMDGPU()
10344 ? 0
10345 : DL.getDefaultGlobalsAddressSpace();
10346 auto Linkage = this->M.getTargetTriple().isWasm()
10349 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10350 Constant::getNullValue(Ty), Elem.first(),
10351 /*InsertBefore=*/nullptr,
10352 GlobalValue::NotThreadLocal, AddressSpaceVal);
10353 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10354 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10355 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10356 Elem.second = GV;
10357 }
10358
10359 return Elem.second;
10360}
10361
10362Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10363 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10364 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10365 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10366}
10367
10369 LLVMContext &Ctx = Builder.getContext();
10370 Value *Null =
10371 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10372 Value *SizeGep =
10373 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10374 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10375 return SizePtrToInt;
10376}
10377
10380 std::string VarName) {
10381 llvm::Constant *MaptypesArrayInit =
10382 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10383 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10384 M, MaptypesArrayInit->getType(),
10385 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10386 VarName);
10387 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10388 return MaptypesArrayGlobal;
10389}
10390
10392 InsertPointTy AllocaIP,
10393 unsigned NumOperands,
10394 struct MapperAllocas &MapperAllocas) {
10395 if (!updateToLocation(Loc))
10396 return;
10397
10398 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10399 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10400 Builder.restoreIP(AllocaIP);
10401 AllocaInst *ArgsBase = Builder.CreateAlloca(
10402 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10403 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10404 ".offload_ptrs");
10405 AllocaInst *ArgSizes = Builder.CreateAlloca(
10406 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10408 MapperAllocas.ArgsBase = ArgsBase;
10409 MapperAllocas.Args = Args;
10410 MapperAllocas.ArgSizes = ArgSizes;
10411}
10412
10414 Function *MapperFunc, Value *SrcLocInfo,
10415 Value *MaptypesArg, Value *MapnamesArg,
10417 int64_t DeviceID, unsigned NumOperands) {
10418 if (!updateToLocation(Loc))
10419 return;
10420
10421 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10422 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10423 Value *ArgsBaseGEP =
10424 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10425 {Builder.getInt32(0), Builder.getInt32(0)});
10426 Value *ArgsGEP =
10427 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10428 {Builder.getInt32(0), Builder.getInt32(0)});
10429 Value *ArgSizesGEP =
10430 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10431 {Builder.getInt32(0), Builder.getInt32(0)});
10432 Value *NullPtr =
10433 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10434 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10435 Builder.getInt32(NumOperands),
10436 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10437 MaptypesArg, MapnamesArg, NullPtr});
10438}
10439
10441 TargetDataRTArgs &RTArgs,
10442 TargetDataInfo &Info,
10443 bool ForEndCall) {
10444 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10445 "expected region end call to runtime only when end call is separate");
10446 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10447 auto VoidPtrTy = UnqualPtrTy;
10448 auto VoidPtrPtrTy = UnqualPtrTy;
10449 auto Int64Ty = Type::getInt64Ty(M.getContext());
10450 auto Int64PtrTy = UnqualPtrTy;
10451
10452 if (!Info.NumberOfPtrs) {
10453 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10454 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10455 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10456 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10457 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10458 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10459 return;
10460 }
10461
10462 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10463 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10464 Info.RTArgs.BasePointersArray,
10465 /*Idx0=*/0, /*Idx1=*/0);
10466 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10467 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10468 /*Idx0=*/0,
10469 /*Idx1=*/0);
10470 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10471 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10472 /*Idx0=*/0, /*Idx1=*/0);
10473 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10474 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10475 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10476 : Info.RTArgs.MapTypesArray,
10477 /*Idx0=*/0,
10478 /*Idx1=*/0);
10479
10480 // Only emit the mapper information arrays if debug information is
10481 // requested.
10482 if (!Info.EmitDebug)
10483 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10484 else
10485 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10486 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10487 /*Idx0=*/0,
10488 /*Idx1=*/0);
10489 // If there is no user-defined mapper, set the mapper array to nullptr to
10490 // avoid an unnecessary data privatization
10491 if (!Info.HasMapper)
10492 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10493 else
10494 RTArgs.MappersArray =
10495 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10496}
10497
10499 InsertPointTy CodeGenIP,
10500 MapInfosTy &CombinedInfo,
10501 TargetDataInfo &Info) {
10503 CombinedInfo.NonContigInfo;
10504
10505 // Build an array of struct descriptor_dim and then assign it to
10506 // offload_args.
10507 //
10508 // struct descriptor_dim {
10509 // uint64_t offset;
10510 // uint64_t count;
10511 // uint64_t stride
10512 // };
10513 Type *Int64Ty = Builder.getInt64Ty();
10515 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10516 "struct.descriptor_dim");
10517
10518 enum { OffsetFD = 0, CountFD, StrideFD };
10519 // We need two index variable here since the size of "Dims" is the same as
10520 // the size of Components, however, the size of offset, count, and stride is
10521 // equal to the size of base declaration that is non-contiguous.
10522 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10523 // Skip emitting ir if dimension size is 1 since it cannot be
10524 // non-contiguous.
10525 if (NonContigInfo.Dims[I] == 1)
10526 continue;
10527 Builder.restoreIP(AllocaIP);
10528 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10529 AllocaInst *DimsAddr =
10530 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10531 Builder.restoreIP(CodeGenIP);
10532 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10533 unsigned RevIdx = EE - II - 1;
10534 Value *DimsLVal = Builder.CreateInBoundsGEP(
10535 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10536 // Offset
10537 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10538 Builder.CreateAlignedStore(
10539 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10540 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10541 // Count
10542 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10543 Builder.CreateAlignedStore(
10544 NonContigInfo.Counts[L][RevIdx], CountLVal,
10545 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10546 // Stride
10547 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10548 Builder.CreateAlignedStore(
10549 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10550 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10551 }
10552 // args[I] = &dims
10553 Builder.restoreIP(CodeGenIP);
10554 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10555 DimsAddr, Builder.getPtrTy());
10556 Value *P = Builder.CreateConstInBoundsGEP2_32(
10557 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10558 Info.RTArgs.PointersArray, 0, I);
10559 Builder.CreateAlignedStore(
10560 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10561 ++L;
10562 }
10563}
10564
10565void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10566 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10567 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10568 BasicBlock *ExitBB, bool IsInit) {
10569 StringRef Prefix = IsInit ? ".init" : ".del";
10570
10571 // Evaluate if this is an array section.
10573 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10574 Value *IsArray =
10575 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10576 Value *DeleteBit = Builder.CreateAnd(
10577 MapType,
10578 Builder.getInt64(
10579 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10580 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10581 Value *DeleteCond;
10582 Value *Cond;
10583 if (IsInit) {
10584 // base != begin?
10585 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10586 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10587 DeleteCond = Builder.CreateIsNull(
10588 DeleteBit,
10589 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10590 } else {
10591 Cond = IsArray;
10592 DeleteCond = Builder.CreateIsNotNull(
10593 DeleteBit,
10594 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10595 }
10596 Cond = Builder.CreateAnd(Cond, DeleteCond);
10597 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10598
10599 emitBlock(BodyBB, MapperFn);
10600 // Get the array size by multiplying element size and element number (i.e., \p
10601 // Size).
10602 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10603 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10604 // memory allocation/deletion purpose only.
10605 Value *MapTypeArg = Builder.CreateAnd(
10606 MapType,
10607 Builder.getInt64(
10608 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10609 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10610 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10611 MapTypeArg = Builder.CreateOr(
10612 MapTypeArg,
10613 Builder.getInt64(
10614 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10615 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10616
10617 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10618 // data structure.
10619 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10620 ArraySize, MapTypeArg, MapName};
10622 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10623 OffloadingArgs);
10624}
10625
10628 llvm::Value *BeginArg)>
10629 GenMapInfoCB,
10630 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10631 bool PreserveMemberOfFlags, bool PropagatePresentToPointee) {
10632 SmallVector<Type *> Params;
10633 Params.emplace_back(Builder.getPtrTy());
10634 Params.emplace_back(Builder.getPtrTy());
10635 Params.emplace_back(Builder.getPtrTy());
10636 Params.emplace_back(Builder.getInt64Ty());
10637 Params.emplace_back(Builder.getInt64Ty());
10638 Params.emplace_back(Builder.getPtrTy());
10639
10640 auto *FnTy =
10641 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10642
10643 SmallString<64> TyStr;
10644 raw_svector_ostream Out(TyStr);
10645 Function *MapperFn =
10647 MapperFn->addFnAttr(Attribute::NoInline);
10648 MapperFn->addFnAttr(Attribute::NoUnwind);
10649 MapperFn->addParamAttr(0, Attribute::NoUndef);
10650 MapperFn->addParamAttr(1, Attribute::NoUndef);
10651 MapperFn->addParamAttr(2, Attribute::NoUndef);
10652 MapperFn->addParamAttr(3, Attribute::NoUndef);
10653 MapperFn->addParamAttr(4, Attribute::NoUndef);
10654 MapperFn->addParamAttr(5, Attribute::NoUndef);
10655
10656 // Start the mapper function code generation.
10657 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10659 Builder.SetInsertPoint(EntryBB);
10660 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
10661
10662 Value *MapperHandle = MapperFn->getArg(0);
10663 Value *BaseIn = MapperFn->getArg(1);
10664 Value *BeginIn = MapperFn->getArg(2);
10665 Value *Size = MapperFn->getArg(3);
10666 Value *MapType = MapperFn->getArg(4);
10667 Value *MapName = MapperFn->getArg(5);
10668
10669 // Compute the starting and end addresses of array elements.
10670 // Prepare common arguments for array initiation and deletion.
10671 // Convert the size in bytes into the number of array elements.
10672 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10673 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10674 Value *PtrBegin = BeginIn;
10675 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10676
10677 // Emit array initiation if this is an array section and \p MapType indicates
10678 // that memory allocation is required.
10679 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10680 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10681 MapType, MapName, ElementSize, HeadBB,
10682 /*IsInit=*/true);
10683
10684 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10685
10686 // Emit the loop header block.
10687 emitBlock(HeadBB, MapperFn);
10688 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10689 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10690 // Evaluate whether the initial condition is satisfied.
10691 Value *IsEmpty =
10692 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10693 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10694
10695 // Emit the loop body block.
10696 emitBlock(BodyBB, MapperFn);
10697 BasicBlock *LastBB = BodyBB;
10698 PHINode *PtrPHI =
10699 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10700 PtrPHI->addIncoming(PtrBegin, HeadBB);
10701
10702 // Get map clause information. Fill up the arrays with all mapped variables.
10703 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10704 if (!Info)
10705 return Info.takeError();
10706
10707 // Call the runtime API __tgt_mapper_num_components to get the number of
10708 // pre-existing components.
10709 Value *OffloadingArgs[] = {MapperHandle};
10710 Value *PreviousSize = createRuntimeFunctionCall(
10711 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10712 OffloadingArgs);
10713 Value *ShiftedPreviousSize =
10714 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10715
10716 // Fill up the runtime mapper handle for all components.
10717 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10718 Value *CurBaseArg = Info->BasePointers[I];
10719 Value *CurBeginArg = Info->Pointers[I];
10720 Value *CurSizeArg = Info->Sizes[I];
10721 Value *CurNameArg = Info->Names.size()
10722 ? Info->Names[I]
10723 : Constant::getNullValue(Builder.getPtrTy());
10724
10725 Value *OriMapType = Builder.getInt64(
10726 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10727 Info->Types[I]));
10728 auto RawType =
10729 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10730 Info->Types[I]);
10731 constexpr uint64_t MemberOfMask =
10732 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10733 constexpr uint64_t AttachBit =
10734 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10735 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10736
10737 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10738 // current array element (N = __tgt_mapper_num_components() at loop body
10739 // start).
10740 //
10741 // Example 1:
10742 // struct S { int x; int *p; };
10743 //
10744 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10745 // use: S arr[2]; ... map(arr)
10746 // entries per element:
10747 //
10748 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10749 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10750 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10751 //
10752 // Example 2:
10753 // struct S1 { int x; int y; };
10754 // struct S2 { int z; S1 *s1p; };
10755 //
10756 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10757 // s2.s1p->y)
10758 // use: S2 arr[2]; ... map(arr)
10759 // entries per element:
10760 //
10761 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10762 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10763 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10764 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10765 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10766 //
10767 // x/y carry inner MEMBER_OF(2)
10768 // which is shifted by N to become MEMBER_OF(N+2).
10769 //
10770 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10771 // the combined ALLOC entry for the s1p->x..y block, and the individual
10772 // x/y entries that are MEMBER_OF that block, all describe storage
10773 // reached through the attach ptr arr[i].s1p.
10774 //
10775 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10776 // linking them to the parent struct:
10777 //
10778 // * (*) Entries with HasAttachPtr: they represent pointee data that
10779 // occupies a different storage block than the struct being mapped, so
10780 // they are not a member of it. They may still be MEMBER_OF an entry
10781 // within that pointee block, in which case those pre-existing bits are
10782 // shifted -- see (***).
10783 // * (**) ATTACH entries: they are not a member of anything — they just
10784 // link a ptr to its ptee.
10785 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10786 // its pre-shaped entries already carry their final MEMBER_OF bits.
10787 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10788 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10789 // it.
10790 //
10791 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10792 // s1p->x/y entries above), those bits are still shifted by N.
10793 Value *MemberMapType;
10794 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10795 Info->HasAttachPtr[I]) {
10796 if (RawType & MemberOfMask)
10797 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10798 else
10799 MemberMapType = OriMapType;
10800 } else {
10801 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10802 }
10803
10804 // Combine the map type inherited from user-defined mapper with that
10805 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10806 // bits of the \a MapType, which is the input argument of the mapper
10807 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10808 // bits of MemberMapType.
10809 // [OpenMP 5.0], 1.2.6. map-type decay.
10810 // | alloc | to | from | tofrom | release | delete
10811 // ----------------------------------------------------------
10812 // alloc | alloc | alloc | alloc | alloc | release | delete
10813 // to | alloc | to | alloc | to | release | delete
10814 // from | alloc | alloc | from | from | release | delete
10815 // tofrom | alloc | to | from | tofrom | release | delete
10816 Value *LeftToFrom = Builder.CreateAnd(
10817 MapType,
10818 Builder.getInt64(
10819 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10820 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10821 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10822 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10823 BasicBlock *AllocElseBB =
10824 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10825 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10826 BasicBlock *ToElseBB =
10827 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10828 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10829 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10830 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10831 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10832 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10833 emitBlock(AllocBB, MapperFn);
10834 Value *AllocMapType = Builder.CreateAnd(
10835 MemberMapType,
10836 Builder.getInt64(
10837 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10838 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10839 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10840 Builder.CreateBr(EndBB);
10841 emitBlock(AllocElseBB, MapperFn);
10842 Value *IsTo = Builder.CreateICmpEQ(
10843 LeftToFrom,
10844 Builder.getInt64(
10845 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10846 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10847 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10848 // In case of to, clear OMP_MAP_FROM.
10849 emitBlock(ToBB, MapperFn);
10850 Value *ToMapType = Builder.CreateAnd(
10851 MemberMapType,
10852 Builder.getInt64(
10853 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10854 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10855 Builder.CreateBr(EndBB);
10856 emitBlock(ToElseBB, MapperFn);
10857 Value *IsFrom = Builder.CreateICmpEQ(
10858 LeftToFrom,
10859 Builder.getInt64(
10860 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10861 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10862 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10863 // In case of from, clear OMP_MAP_TO.
10864 emitBlock(FromBB, MapperFn);
10865 Value *FromMapType = Builder.CreateAnd(
10866 MemberMapType,
10867 Builder.getInt64(
10868 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10869 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10870 // In case of tofrom, do nothing.
10871 emitBlock(EndBB, MapperFn);
10872 LastBB = EndBB;
10873 PHINode *CurMapType =
10874 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10875 CurMapType->addIncoming(AllocMapType, AllocBB);
10876 CurMapType->addIncoming(ToMapType, ToBB);
10877 CurMapType->addIncoming(FromMapType, FromBB);
10878 CurMapType->addIncoming(MemberMapType, ToElseBB);
10879
10880 // Propagate map-type-modifying bits from the outer map clause to each map
10881 // inserted by the mapper.
10882 //
10883 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10884 // list item from the map clause and to apply the clauses specified in the
10885 // declared mapper to the construct on which the map clause appears...
10886 // If any modifier with the map-type-modifying property appears in the map
10887 // clause then the effect is as if that modifier appears in each map clause
10888 // specified in the declared mapper.
10889 //
10890 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10891 //
10892 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
10893 //
10894 // PRESENT is propagated only to entries that have an attach ptr
10895 // (HasAttachPtr): the pointee data, which occupies a different storage
10896 // block than the struct being mapped and so is not covered by the
10897 // present-check on the struct's own storage. A present modifier on the
10898 // outer clause must still require that pointee to be present on the device.
10899 //
10900 // This is gated on \p PropagatePresentToPointee (set by callers only for
10901 // OpenMP >= 6.0). Before 6.0 the present modifier is treated as not
10902 // applying to the pointee: the spec committee confirmed the divergence
10903 // between the present "motion" modifier (to/from) and the present map-type
10904 // modifier (map) was unintentional, to be fixed as an OpenMP 6.0 erratum,
10905 // so for 5.2 present is ignored for the pointee for both map and to/from.
10906 //
10907 // TODO: PRESENT should also be propagated to the struct's own members
10908 // (e.g. the s.x, s.y of map(present, mapper(id): s)) so that an absent
10909 // member triggers the present-check. We cannot do that yet: while pointer
10910 // members are mapped with PTR_AND_OBJ, a single combined entry allocates
10911 // the whole struct (including the pointer's storage), so propagating
10912 // PRESENT to it would wrongly require the pointer's pointee to be present.
10913 // Enable member propagation once Clang stops emitting PTR_AND_OBJ and uses
10914 // attach-style maps throughout.
10915 uint64_t ModifierBits =
10916 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10917 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
10918 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
10919 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
10920 if (PropagatePresentToPointee && Info->HasAttachPtr[I])
10921 ModifierBits |=
10922 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10923 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10924 Value *ImportedModifierBits =
10925 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
10926 Value *CurMapTypeWithModifiers = Builder.CreateOr(
10927 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
10928
10929 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
10930 // reserved for the attach(always) map-type modifier, and other modifier
10931 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
10932 Value *FinalMapType =
10933 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
10934
10935 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
10936 CurSizeArg, FinalMapType, CurNameArg};
10937
10938 auto ChildMapperFn = CustomMapperCB(I);
10939 if (!ChildMapperFn)
10940 return ChildMapperFn.takeError();
10941 if (*ChildMapperFn) {
10942 // Call the corresponding mapper function.
10943 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
10944 ->setDoesNotThrow();
10945 } else {
10946 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10947 // data structure.
10949 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10950 OffloadingArgs);
10951 }
10952 }
10953
10954 // Update the pointer to point to the next element that needs to be mapped,
10955 // and check whether we have mapped all elements.
10956 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
10957 "omp.arraymap.next");
10958 PtrPHI->addIncoming(PtrNext, LastBB);
10959 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
10960 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
10961 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
10962
10963 emitBlock(ExitBB, MapperFn);
10964 // Emit array deletion if this is an array section and \p MapType indicates
10965 // that deletion is required.
10966 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10967 MapType, MapName, ElementSize, DoneBB,
10968 /*IsInit=*/false);
10969
10970 // Emit the function exit block.
10971 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
10972
10973 Builder.CreateRetVoid();
10974 return MapperFn;
10975}
10976
10978 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
10979 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
10980 bool IsNonContiguous,
10981 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10982
10983 // Reset the array information.
10984 Info.clearArrayInfo();
10985 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
10986
10987 if (Info.NumberOfPtrs == 0)
10988 return Error::success();
10989
10990 Builder.restoreIP(AllocaIP);
10991 // Detect if we have any capture size requiring runtime evaluation of the
10992 // size so that a constant array could be eventually used.
10993 ArrayType *PointerArrayType =
10994 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
10995
10996 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
10997 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
10998
10999 Info.RTArgs.PointersArray = Builder.CreateAlloca(
11000 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
11001 AllocaInst *MappersArray = Builder.CreateAlloca(
11002 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
11003 Info.RTArgs.MappersArray = MappersArray;
11004
11005 // If we don't have any VLA types or other types that require runtime
11006 // evaluation, we can use a constant array for the map sizes, otherwise we
11007 // need to fill up the arrays as we do for the pointers.
11008 Type *Int64Ty = Builder.getInt64Ty();
11009 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
11010 ConstantInt::get(Int64Ty, 0));
11011 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
11012 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
11013 bool IsNonContigEntry =
11014 IsNonContiguous &&
11015 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11016 CombinedInfo.Types[I] &
11017 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
11018 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
11019 // descriptor_dim records), not the byte size.
11020 if (IsNonContigEntry) {
11021 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
11022 "Index must be in-bounds for NON_CONTIG Dims array");
11023 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
11024 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
11025 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
11026 continue;
11027 }
11028 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
11029 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
11030 ConstSizes[I] = CI;
11031 continue;
11032 }
11033 }
11034 RuntimeSizes.set(I);
11035 }
11036
11037 if (RuntimeSizes.all()) {
11038 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11039 Info.RTArgs.SizesArray = Builder.CreateAlloca(
11040 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11041 restoreIPandDebugLoc(Builder, CodeGenIP);
11042 } else {
11043 auto *SizesArrayInit = ConstantArray::get(
11044 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
11045 std::string Name = createPlatformSpecificName({"offload_sizes"});
11046 auto *SizesArrayGbl =
11047 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
11048 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
11049 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
11050
11051 if (!RuntimeSizes.any()) {
11052 Info.RTArgs.SizesArray = SizesArrayGbl;
11053 } else {
11054 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11055 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
11056 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11057 AllocaInst *Buffer = Builder.CreateAlloca(
11058 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11059 Buffer->setAlignment(OffloadSizeAlign);
11060 restoreIPandDebugLoc(Builder, CodeGenIP);
11061 Builder.CreateMemCpy(
11062 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11063 SizesArrayGbl, OffloadSizeAlign,
11064 Builder.getIntN(
11065 IndexSize,
11066 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11067
11068 Info.RTArgs.SizesArray = Buffer;
11069 }
11070 restoreIPandDebugLoc(Builder, CodeGenIP);
11071 }
11072
11073 // The map types are always constant so we don't need to generate code to
11074 // fill arrays. Instead, we create an array constant.
11076 for (auto mapFlag : CombinedInfo.Types)
11077 Mapping.push_back(
11078 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11079 mapFlag));
11080 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11081 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11082 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11083
11084 // The information types are only built if provided.
11085 if (!CombinedInfo.Names.empty()) {
11086 auto *MapNamesArrayGbl = createOffloadMapnames(
11087 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11088 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11089 Info.EmitDebug = true;
11090 } else {
11091 Info.RTArgs.MapNamesArray =
11093 Info.EmitDebug = false;
11094 }
11095
11096 // If there's a present map type modifier, it must not be applied to the end
11097 // of a region, so generate a separate map type array in that case.
11098 if (Info.separateBeginEndCalls()) {
11099 bool EndMapTypesDiffer = false;
11100 for (uint64_t &Type : Mapping) {
11101 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11102 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11103 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11104 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11105 EndMapTypesDiffer = true;
11106 }
11107 }
11108 if (EndMapTypesDiffer) {
11109 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11110 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11111 }
11112 }
11113
11114 PointerType *PtrTy = Builder.getPtrTy();
11115 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11116 Value *BPVal = CombinedInfo.BasePointers[I];
11117 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11118 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11119 0, I);
11120 Builder.CreateAlignedStore(BPVal, BP,
11121 M.getDataLayout().getPrefTypeAlign(PtrTy));
11122
11123 if (Info.requiresDevicePointerInfo()) {
11124 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11125 CodeGenIP = Builder.saveIP();
11126 Builder.restoreIP(AllocaIP);
11127 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11128 restoreIPandDebugLoc(Builder, CodeGenIP);
11129 if (DeviceAddrCB)
11130 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11131 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11132 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11133 if (DeviceAddrCB)
11134 DeviceAddrCB(I, BP);
11135 }
11136 }
11137
11138 Value *PVal = CombinedInfo.Pointers[I];
11139 Value *P = Builder.CreateConstInBoundsGEP2_32(
11140 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11141 I);
11142 // TODO: Check alignment correct.
11143 Builder.CreateAlignedStore(PVal, P,
11144 M.getDataLayout().getPrefTypeAlign(PtrTy));
11145
11146 if (RuntimeSizes.test(I)) {
11147 Value *S = Builder.CreateConstInBoundsGEP2_32(
11148 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11149 /*Idx0=*/0,
11150 /*Idx1=*/I);
11151 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11152 Int64Ty,
11153 /*isSigned=*/true),
11154 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11155 }
11156 // Fill up the mapper array.
11157 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11158 Value *MFunc = ConstantPointerNull::get(PtrTy);
11159
11160 auto CustomMFunc = CustomMapperCB(I);
11161 if (!CustomMFunc)
11162 return CustomMFunc.takeError();
11163 if (*CustomMFunc)
11164 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11165
11166 Value *MAddr = Builder.CreateInBoundsGEP(
11167 PointerArrayType, MappersArray,
11168 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11169 Builder.CreateAlignedStore(
11170 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11171 }
11172
11173 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11174 Info.NumberOfPtrs == 0)
11175 return Error::success();
11176 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11177 return Error::success();
11178}
11179
11181 BasicBlock *CurBB = Builder.GetInsertBlock();
11182
11183 if (!CurBB || CurBB->hasTerminator()) {
11184 // If there is no insert point or the previous block is already
11185 // terminated, don't touch it.
11186 } else {
11187 // Otherwise, create a fall-through branch.
11188 Builder.CreateBr(Target);
11189 }
11190
11191 Builder.ClearInsertionPoint();
11192}
11193
11195 bool IsFinished) {
11196 BasicBlock *CurBB = Builder.GetInsertBlock();
11197
11198 // Fall out of the current block (if necessary).
11199 emitBranch(BB);
11200
11201 if (IsFinished && BB->use_empty()) {
11202 BB->eraseFromParent();
11203 return;
11204 }
11205
11206 // Place the block after the current block, if possible, or else at
11207 // the end of the function.
11208 if (CurBB && CurBB->getParent())
11209 CurFn->insert(std::next(CurBB->getIterator()), BB);
11210 else
11211 CurFn->insert(CurFn->end(), BB);
11212 Builder.SetInsertPoint(BB);
11213}
11214
11216 BodyGenCallbackTy ElseGen,
11217 InsertPointTy AllocaIP,
11218 ArrayRef<BasicBlock *> DeallocBlocks) {
11219 // If the condition constant folds and can be elided, try to avoid emitting
11220 // the condition and the dead arm of the if/else.
11221 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11222 auto CondConstant = CI->getSExtValue();
11223 if (CondConstant)
11224 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11225
11226 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11227 }
11228
11229 Function *CurFn = Builder.GetInsertBlock()->getParent();
11230
11231 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11232 // emit the conditional branch.
11233 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11234 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11235 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11236 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11237 // Emit the 'then' code.
11238 emitBlock(ThenBlock, CurFn);
11239 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11240 return Err;
11241 emitBranch(ContBlock);
11242 // Emit the 'else' code if present.
11243 // There is no need to emit line number for unconditional branch.
11244 emitBlock(ElseBlock, CurFn);
11245 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11246 return Err;
11247 // There is no need to emit line number for unconditional branch.
11248 emitBranch(ContBlock);
11249 // Emit the continuation block for code after the if.
11250 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11251 return Error::success();
11252}
11253
11254bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11255 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11258 "Unexpected Atomic Ordering.");
11259
11260 bool Flush = false;
11262
11263 switch (AK) {
11264 case Read:
11267 FlushAO = AtomicOrdering::Acquire;
11268 Flush = true;
11269 }
11270 break;
11271 case Write:
11272 case Compare:
11273 case Update:
11276 FlushAO = AtomicOrdering::Release;
11277 Flush = true;
11278 }
11279 break;
11280 case Capture:
11281 switch (AO) {
11283 FlushAO = AtomicOrdering::Acquire;
11284 Flush = true;
11285 break;
11287 FlushAO = AtomicOrdering::Release;
11288 Flush = true;
11289 break;
11293 Flush = true;
11294 break;
11295 default:
11296 // do nothing - leave silently.
11297 break;
11298 }
11299 }
11300
11301 if (Flush) {
11302 // Currently Flush RT call still doesn't take memory_ordering, so for when
11303 // that happens, this tries to do the resolution of which atomic ordering
11304 // to use with but issue the flush call
11305 // TODO: pass `FlushAO` after memory ordering support is added
11306 (void)FlushAO;
11307 emitFlush(Loc);
11308 }
11309
11310 // for AO == AtomicOrdering::Monotonic and all other case combinations
11311 // do nothing
11312 return Flush;
11313}
11314
11318 AtomicOrdering AO, InsertPointTy AllocaIP) {
11319 if (!updateToLocation(Loc))
11320 return Loc.IP;
11321
11322 assert(X.Var->getType()->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 read expected a scalar type");
11328
11329 Value *XRead = nullptr;
11330
11331 if (XElemTy->isIntegerTy()) {
11332 LoadInst *XLD =
11333 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11334 XLD->setAtomic(AO);
11335 XRead = cast<Value>(XLD);
11336 } else if (XElemTy->isStructTy()) {
11337 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11338 // target does not support `atomicrmw` of the size of the struct
11339 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11340 OldVal->setAtomic(AO);
11341 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11342 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11343 OpenMPIRBuilder::AtomicInfo atomicInfo(
11344 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11345 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11346 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11347 XRead = AtomicLoadRes.first;
11348 OldVal->eraseFromParent();
11349 } else {
11350 // We need to perform atomic op as integer
11351 IntegerType *IntCastTy =
11352 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11353 LoadInst *XLoad =
11354 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11355 XLoad->setAtomic(AO);
11356 if (XElemTy->isFloatingPointTy()) {
11357 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11358 } else {
11359 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11360 }
11361 }
11362 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11363 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11364 return Builder.saveIP();
11365}
11366
11369 AtomicOpValue &X, Value *Expr,
11370 AtomicOrdering AO, InsertPointTy AllocaIP) {
11371 if (!updateToLocation(Loc))
11372 return Loc.IP;
11373
11374 assert(X.Var->getType()->isPointerTy() &&
11375 "OMP Atomic expects a pointer to target memory");
11376 Type *XElemTy = X.ElemTy;
11377 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11378 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11379 "OMP atomic write expected a scalar type");
11380
11381 if (XElemTy->isIntegerTy()) {
11382 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11383 XSt->setAtomic(AO);
11384 } else if (XElemTy->isStructTy()) {
11385 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11386 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11387 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11388 OpenMPIRBuilder::AtomicInfo atomicInfo(
11389 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11390 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11391 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11392 OldVal->eraseFromParent();
11393 } else {
11394 // We need to bitcast and perform atomic op as integers
11395 IntegerType *IntCastTy =
11396 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11397 Value *ExprCast =
11398 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11399 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11400 XSt->setAtomic(AO);
11401 }
11402
11403 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11404 return Builder.saveIP();
11405}
11406
11409 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11410 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11411 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11412 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11413 if (!updateToLocation(Loc))
11414 return Loc.IP;
11415
11416 LLVM_DEBUG({
11417 Type *XTy = X.Var->getType();
11418 assert(XTy->isPointerTy() &&
11419 "OMP Atomic expects a pointer to target memory");
11420 Type *XElemTy = X.ElemTy;
11421 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11422 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11423 "OMP atomic update expected a scalar or struct type");
11424 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11425 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&
11426 "OpenMP atomic does not support LT or GT operations");
11427 });
11428
11429 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11430 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11431 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11432 if (!AtomicResult)
11433 return AtomicResult.takeError();
11434 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11435 return Builder.saveIP();
11436}
11437
11438// FIXME: Duplicating AtomicExpand
11439Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11440 AtomicRMWInst::BinOp RMWOp) {
11441 switch (RMWOp) {
11442 case AtomicRMWInst::Add:
11443 return Builder.CreateAdd(Src1, Src2);
11444 case AtomicRMWInst::Sub:
11445 return Builder.CreateSub(Src1, Src2);
11446 case AtomicRMWInst::And:
11447 return Builder.CreateAnd(Src1, Src2);
11449 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11450 case AtomicRMWInst::Or:
11451 return Builder.CreateOr(Src1, Src2);
11452 case AtomicRMWInst::Xor:
11453 return Builder.CreateXor(Src1, Src2);
11458 case AtomicRMWInst::Max:
11459 case AtomicRMWInst::Min:
11472 llvm_unreachable("Unsupported atomic update operation");
11473 }
11474 llvm_unreachable("Unsupported atomic update operation");
11475}
11476
11478 // Loads cannot use Release or AcquireRelease ordering. This load is
11479 // just the initial value for the cmpxchg loop; the cmpxchg itself
11480 // retains the original ordering.
11481 AtomicOrdering LoadAO = AO;
11482
11483 if (AO == AtomicOrdering::Release) {
11485 } else if (AO == AtomicOrdering::AcquireRelease) {
11486 LoadAO = AtomicOrdering::Acquire;
11487 }
11488
11489 return LoadAO;
11490}
11491
11492Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11493 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11495 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11496 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11497 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11498 bool emitRMWOp = false;
11499 switch (RMWOp) {
11500 case AtomicRMWInst::Add:
11501 case AtomicRMWInst::And:
11503 case AtomicRMWInst::Or:
11504 case AtomicRMWInst::Xor:
11506 emitRMWOp = XElemTy;
11507 break;
11508 case AtomicRMWInst::Sub:
11509 emitRMWOp = (IsXBinopExpr && XElemTy);
11510 break;
11511 default:
11512 emitRMWOp = false;
11513 }
11514 emitRMWOp &= XElemTy->isIntegerTy();
11515
11516 std::pair<Value *, Value *> Res;
11517 if (emitRMWOp) {
11518 AtomicRMWInst *RMWInst =
11519 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11520 if (T.isAMDGPU()) {
11521 if (IsIgnoreDenormalMode)
11522 RMWInst->setMetadata("amdgpu.ignore.denormal.mode",
11523 llvm::MDNode::get(Builder.getContext(), {}));
11524 if (!IsFineGrainedMemory)
11525 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11526 llvm::MDNode::get(Builder.getContext(), {}));
11527 if (!IsRemoteMemory)
11528 RMWInst->setMetadata("amdgpu.no.remote.memory",
11529 llvm::MDNode::get(Builder.getContext(), {}));
11530 }
11531 Res.first = RMWInst;
11532 // not needed except in case of postfix captures. Generate anyway for
11533 // consistency with the else part. Will be removed with any DCE pass.
11534 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11535 if (RMWOp == AtomicRMWInst::Xchg)
11536 Res.second = Res.first;
11537 else
11538 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11539 } else if (XElemTy->isStructTy()) {
11540 LoadInst *OldVal =
11541 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11543 OldVal->setAtomic(LoadAO);
11544 const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
11545 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11546
11547 OpenMPIRBuilder::AtomicInfo atomicInfo(
11548 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11549 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11550 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11551 BasicBlock *CurBB = Builder.GetInsertBlock();
11552 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11553 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11554 BasicBlock *ExitBB =
11555 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11556 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11557 X->getName() + ".atomic.cont");
11558 ContBB->getTerminator()->eraseFromParent();
11559 Builder.restoreIP(AllocaIP);
11560 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11561 NewAtomicAddr->setName(X->getName() + "x.new.val");
11562 Builder.SetInsertPoint(ContBB);
11563 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11564 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11565 Value *OldExprVal = PHI;
11566 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11567 if (!CBResult)
11568 return CBResult.takeError();
11569 Value *Upd = *CBResult;
11570 Builder.CreateStore(Upd, NewAtomicAddr);
11573 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11574 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11575 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11576 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11577 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11578 OldVal->eraseFromParent();
11579 Res.first = OldExprVal;
11580 Res.second = Upd;
11581
11582 if (UnreachableInst *ExitTI =
11584 CurBBTI->eraseFromParent();
11585 Builder.SetInsertPoint(ExitBB);
11586 } else {
11587 Builder.SetInsertPoint(ExitTI);
11588 }
11589 } else {
11590 IntegerType *IntCastTy =
11591 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11592 LoadInst *OldVal =
11593 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11595 OldVal->setAtomic(LoadAO);
11596 // CurBB
11597 // | /---\
11598 // ContBB |
11599 // | \---/
11600 // ExitBB
11601 BasicBlock *CurBB = Builder.GetInsertBlock();
11602 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11603 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11604 BasicBlock *ExitBB =
11605 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11606 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11607 X->getName() + ".atomic.cont");
11608 ContBB->getTerminator()->eraseFromParent();
11609 Builder.restoreIP(AllocaIP);
11610 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11611 NewAtomicAddr->setName(X->getName() + "x.new.val");
11612 Builder.SetInsertPoint(ContBB);
11613 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11614 PHI->addIncoming(OldVal, CurBB);
11615 bool IsIntTy = XElemTy->isIntegerTy();
11616 Value *OldExprVal = PHI;
11617 if (!IsIntTy) {
11618 if (XElemTy->isFloatingPointTy()) {
11619 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11620 X->getName() + ".atomic.fltCast");
11621 } else {
11622 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11623 X->getName() + ".atomic.ptrCast");
11624 }
11625 }
11626
11627 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11628 if (!CBResult)
11629 return CBResult.takeError();
11630 Value *Upd = *CBResult;
11631 Builder.CreateStore(Upd, NewAtomicAddr);
11632 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11635 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11636 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11637 Result->setVolatile(VolatileX);
11638 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11639 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11640 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11641 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11642
11643 Res.first = OldExprVal;
11644 Res.second = Upd;
11645
11646 // set Insertion point in exit block
11647 if (UnreachableInst *ExitTI =
11649 CurBBTI->eraseFromParent();
11650 Builder.SetInsertPoint(ExitBB);
11651 } else {
11652 Builder.SetInsertPoint(ExitTI);
11653 }
11654 }
11655
11656 return Res;
11657}
11658
11661 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11662 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11663 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11664 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11665 if (!updateToLocation(Loc))
11666 return Loc.IP;
11667
11668 LLVM_DEBUG({
11669 Type *XTy = X.Var->getType();
11670 assert(XTy->isPointerTy() &&
11671 "OMP Atomic expects a pointer to target memory");
11672 Type *XElemTy = X.ElemTy;
11673 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11674 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11675 "OMP atomic capture expected a scalar or struct type");
11676 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11677 "OpenMP atomic does not support LT or GT operations");
11678 });
11679
11680 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11681 // 'x' is simply atomically rewritten with 'expr'.
11682 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11683 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11684 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11685 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11686 if (!AtomicResult)
11687 return AtomicResult.takeError();
11688 Value *CapturedVal =
11689 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11690 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11691
11692 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11693 return Builder.saveIP();
11694}
11695
11699 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11700 bool IsFailOnly, bool IsWeak) {
11701
11703 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11704 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11705}
11706
11710 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11711 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11712
11713 if (!updateToLocation(Loc))
11714 return Loc.IP;
11715
11716 assert(X.Var->getType()->isPointerTy() &&
11717 "OMP atomic expects a pointer to target memory");
11718 // compare capture
11719 if (V.Var) {
11720 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11721 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11722 }
11723
11724 bool IsInteger = E->getType()->isIntegerTy();
11725
11726 if (Op == OMPAtomicCompareOp::EQ) {
11727 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11728 // R.Var handling.
11729 Value *OldValue = nullptr;
11730 Value *SuccessOrFail = nullptr;
11731
11732 if (!IsInteger && HandleFPNegZero) {
11733 // IEEE 754 special cases for cmpxchg (which is bitwise):
11734 // 1. -0.0 == +0.0 but they have different bit patterns.
11735 // 2. NaN != NaN but identical NaN bit patterns would match.
11736 //
11737 // CurBB:
11738 // %e_int = bitcast E to intN
11739 // %d_int = bitcast D to intN
11740 // %x_curr = load atomic intN, X
11741 // %x_fp = bitcast %x_curr to FP
11742 // %e_is_nan = fcmp uno E, E
11743 // %x_is_nan = fcmp uno %x_fp, %x_fp
11744 // %either_nan = or %e_is_nan, %x_is_nan
11745 // br %either_nan, NaNBB, NotNaNBB
11746 // NaNBB: ; NaN == anything is always false
11747 // br ExitBB
11748 // NotNaNBB:
11749 // %x_is_zero = fcmp oeq %x_fp, 0.0
11750 // %e_is_zero = fcmp oeq E, 0.0
11751 // %both_zero = and %x_is_zero, %e_is_zero
11752 // br %both_zero, ZeroBB, NormalBB
11753 // ZeroBB: ; both ±0.0 → x = d
11754 // cmpxchg X, %x_curr, %d_int
11755 // br ExitBB
11756 // NormalBB: ; original path
11757 // cmpxchg X, %e_int, %d_int
11758 // br ExitBB
11759 // ExitBB:
11760 // phi merge
11761 IntegerType *IntCastTy =
11762 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11763 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11764 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11765
11766 // Load X atomically.
11767 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11768 X.Var->getName() + ".atomic.load");
11770 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11771
11772 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11773 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11774 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11775 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11776 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11777
11778 BasicBlock *CurBB = Builder.GetInsertBlock();
11779 Function *F = CurBB->getParent();
11780 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11781 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11782 BasicBlock *ExitBB =
11783 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11785 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11786 BasicBlock *NotNaNBB = BasicBlock::Create(
11787 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11789 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11790 BasicBlock *NormalBB = BasicBlock::Create(
11791 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11792
11793 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11794 CurBB->getTerminator()->eraseFromParent();
11795 Builder.SetInsertPoint(CurBB);
11796 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11797
11798 // NaNBB: NaN == anything is always false; skip cmpxchg.
11799 Builder.SetInsertPoint(NaNBB);
11800 Builder.CreateBr(ExitBB);
11801
11802 // NotNaNBB: check both X and E for ±0.0.
11803 Builder.SetInsertPoint(NotNaNBB);
11804 Value *XIsZero =
11805 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11806 X.Var->getName() + ".atomic.xiszero");
11807 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11808 "atomic.e.iszero");
11809 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11810 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11811
11812 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11813 Builder.SetInsertPoint(ZeroBB);
11814 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11815 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11816 ResZero->setWeak(IsWeak);
11817 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11818 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11819 Builder.CreateBr(ExitBB);
11820
11821 // NormalBB: original bitwise cmpxchg.
11822 Builder.SetInsertPoint(NormalBB);
11823 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11824 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11825 ResNormal->setWeak(IsWeak);
11826 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11827 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11828 Builder.CreateBr(ExitBB);
11829
11830 // ExitBB: merge results from NaN, Zero, and Normal paths.
11831 Builder.SetInsertPoint(ExitBB, ExitBB->begin());
11832 PHINode *OldIntPHI =
11833 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11834 OldIntPHI->addIncoming(XCurr, NaNBB);
11835 OldIntPHI->addIncoming(OldZero, ZeroBB);
11836 OldIntPHI->addIncoming(OldNormal, NormalBB);
11837 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11838 X.Var->getName() + ".atomic.ok");
11839 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11840 SuccessPHI->addIncoming(OkZero, ZeroBB);
11841 SuccessPHI->addIncoming(OkNormal, NormalBB);
11842
11843 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11844 CurBBTI->eraseFromParent();
11845 Builder.SetInsertPoint(ExitBB);
11846 } else {
11847 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11848 }
11849
11850 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11851 X.Var->getName() + ".atomic.old.fp");
11852 SuccessOrFail = SuccessPHI;
11853 } else {
11854 AtomicCmpXchgInst *Result = nullptr;
11855 if (!IsInteger) {
11856 IntegerType *IntCastTy =
11857 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11858 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11859 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11860 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11861 MaybeAlign(), AO, Failure);
11862 } else {
11863 Result =
11864 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11865 }
11866 Result->setWeak(IsWeak);
11867
11868 if (V.Var) {
11869 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11870 if (!IsInteger)
11871 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11872 assert(OldValue->getType() == V.ElemTy &&
11873 "OldValue and V must be of same type");
11874 if (IsPostfixUpdate) {
11875 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11876 } else {
11877 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11878 if (IsFailOnly) {
11879 BasicBlock *CurBB = Builder.GetInsertBlock();
11880 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11881 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11882 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11883 CurBBTI, X.Var->getName() + ".atomic.exit");
11884 BasicBlock *ContBB = CurBB->splitBasicBlock(
11885 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11886 ContBB->getTerminator()->eraseFromParent();
11887 CurBB->getTerminator()->eraseFromParent();
11888
11889 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11890
11891 Builder.SetInsertPoint(ContBB);
11892 Builder.CreateStore(OldValue, V.Var);
11893 Builder.CreateBr(ExitBB);
11894
11895 if (UnreachableInst *ExitTI =
11897 CurBBTI->eraseFromParent();
11898 Builder.SetInsertPoint(ExitBB);
11899 } else {
11900 Builder.SetInsertPoint(ExitTI);
11901 }
11902 } else {
11903 Value *CapturedValue =
11904 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11905 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11906 }
11907 }
11908 }
11909 // The comparison result has to be stored.
11910 if (R.Var) {
11911 assert(R.Var->getType()->isPointerTy() &&
11912 "r.var must be of pointer type");
11913 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11914
11915 Value *SuccessFailureVal =
11916 Builder.CreateExtractValue(Result, /*Idxs=*/1);
11917 Value *ResultCast =
11918 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
11919 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
11920 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11921 }
11922 }
11923
11924 // For the HandleFPNegZero path, handle V.Var and R.Var using the
11925 // pre-computed OldValue and SuccessOrFail.
11926 if (HandleFPNegZero && !IsInteger) {
11927 if (V.Var) {
11928 assert(OldValue->getType() == V.ElemTy &&
11929 "OldValue and V must be of same type");
11930 if (IsPostfixUpdate) {
11931 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11932 } else {
11933 if (IsFailOnly) {
11934 BasicBlock *CurBB = Builder.GetInsertBlock();
11935 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11936 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11937 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11938 CurBBTI, X.Var->getName() + ".atomic.exit");
11939 BasicBlock *ContBB = CurBB->splitBasicBlock(
11940 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11941 ContBB->getTerminator()->eraseFromParent();
11942 CurBB->getTerminator()->eraseFromParent();
11943
11944 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11945
11946 Builder.SetInsertPoint(ContBB);
11947 Builder.CreateStore(OldValue, V.Var);
11948 Builder.CreateBr(ExitBB);
11949
11950 if (UnreachableInst *ExitTI =
11952 CurBBTI->eraseFromParent();
11953 Builder.SetInsertPoint(ExitBB);
11954 } else {
11955 Builder.SetInsertPoint(ExitTI);
11956 }
11957 } else {
11958 Value *CapturedValue =
11959 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11960 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11961 }
11962 }
11963 }
11964 // The comparison result has to be stored.
11965 if (R.Var) {
11966 assert(R.Var->getType()->isPointerTy() &&
11967 "r.var must be of pointer type");
11968 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11969
11970 Value *ResultCast = R.IsSigned
11971 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
11972 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
11973 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11974 }
11975 }
11976 } else {
11977 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
11978 "Op should be either max or min at this point");
11979 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
11980
11981 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
11982 // Let's take max as example.
11983 // OpenMP form:
11984 // x = x > expr ? expr : x;
11985 // LLVM form:
11986 // *ptr = *ptr > val ? *ptr : val;
11987 // We need to transform to LLVM form.
11988 // x = x <= expr ? x : expr;
11990 if (IsXBinopExpr) {
11991 if (IsInteger) {
11992 if (X.IsSigned)
11993 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
11995 else
11996 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
11998 } else {
11999 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
12001 }
12002 } else {
12003 if (IsInteger) {
12004 if (X.IsSigned)
12005 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
12007 else
12008 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
12010 } else {
12011 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
12013 }
12014 }
12015
12016 AtomicRMWInst *OldValue =
12017 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
12018 if (V.Var) {
12019 Value *CapturedValue = nullptr;
12020 if (IsPostfixUpdate) {
12021 CapturedValue = OldValue;
12022 } else {
12023 CmpInst::Predicate Pred;
12024 switch (NewOp) {
12025 case AtomicRMWInst::Max:
12026 Pred = CmpInst::ICMP_SGT;
12027 break;
12029 Pred = CmpInst::ICMP_UGT;
12030 break;
12032 Pred = CmpInst::FCMP_OGT;
12033 break;
12034 case AtomicRMWInst::Min:
12035 Pred = CmpInst::ICMP_SLT;
12036 break;
12038 Pred = CmpInst::ICMP_ULT;
12039 break;
12041 Pred = CmpInst::FCMP_OLT;
12042 break;
12043 default:
12044 llvm_unreachable("unexpected comparison op");
12045 }
12046 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
12047 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
12048 }
12049 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12050 }
12051 }
12052
12053 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
12054
12055 return Builder.saveIP();
12056}
12057
12060 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12061 Value *NumTeamsUpper, Value *ThreadLimit,
12062 Value *IfExpr) {
12063 if (!updateToLocation(Loc))
12064 return InsertPointTy();
12065
12066 uint32_t SrcLocStrSize;
12067 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12068 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12069 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12070
12071 // Outer allocation basicblock is the entry block of the current function.
12072 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12073 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12074 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12075 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12076 }
12077
12078 // The current basic block is split into four basic blocks. After outlining,
12079 // they will be mapped as follows:
12080 // ```
12081 // def current_fn() {
12082 // current_basic_block:
12083 // br label %teams.exit
12084 // teams.exit:
12085 // ; instructions after teams
12086 // }
12087 //
12088 // def outlined_fn() {
12089 // teams.alloca:
12090 // br label %teams.body
12091 // teams.body:
12092 // ; instructions within teams body
12093 // }
12094 // ```
12095 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12096 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12097 BasicBlock *AllocaBB =
12098 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12099
12100 bool SubClausesPresent =
12101 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12102 // Push num_teams
12103 if (!Config.isTargetDevice() && SubClausesPresent) {
12104 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12105 "if lowerbound is non-null, then upperbound must also be non-null "
12106 "for bounds on num_teams");
12107
12108 if (NumTeamsUpper == nullptr)
12109 NumTeamsUpper = Builder.getInt32(0);
12110
12111 if (NumTeamsLower == nullptr)
12112 NumTeamsLower = NumTeamsUpper;
12113
12114 if (IfExpr) {
12115 assert(IfExpr->getType()->isIntegerTy() &&
12116 "argument to if clause must be an integer value");
12117
12118 // upper = ifexpr ? upper : 1
12119 if (IfExpr->getType() != Int1)
12120 IfExpr = Builder.CreateICmpNE(IfExpr,
12121 ConstantInt::get(IfExpr->getType(), 0));
12122 NumTeamsUpper = Builder.CreateSelect(
12123 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12124
12125 // lower = ifexpr ? lower : 1
12126 NumTeamsLower = Builder.CreateSelect(
12127 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12128 }
12129
12130 if (ThreadLimit == nullptr)
12131 ThreadLimit = Builder.getInt32(0);
12132
12133 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12134 // truncate or sign extend the passed values to match the int32 parameters.
12135 Value *NumTeamsLowerInt32 =
12136 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12137 Value *NumTeamsUpperInt32 =
12138 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12139 Value *ThreadLimitInt32 =
12140 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12141
12142 Value *ThreadNum = getOrCreateThreadID(Ident);
12143
12145 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12146 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12147 ThreadLimitInt32});
12148 }
12149 // Generate the body of teams.
12150 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12151 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12152 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12153 return Err;
12154
12155 auto OI = std::make_unique<OutlineInfo>();
12156 OI->EntryBB = AllocaBB;
12157 OI->ExitBB = ExitBB;
12158 OI->OuterAllocBB = &OuterAllocaBB;
12159
12160 // Insert fake values for global tid and bound tid.
12162 InsertPointTy OuterAllocaIP(&OuterAllocaBB, OuterAllocaBB.begin());
12163 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12164 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12165 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12166 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12167
12168 auto HostPostOutlineCB = [this, Ident,
12169 ToBeDeleted](Function &OutlinedFn) mutable {
12170 // The stale call instruction will be replaced with a new call instruction
12171 // for runtime call with the outlined function.
12172
12173 assert(OutlinedFn.hasOneUse() &&
12174 "there must be a single user for the outlined function");
12175 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12176 ToBeDeleted.push_back(StaleCI);
12177
12178 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12179 "Outlined function must have two or three arguments only");
12180
12181 bool HasShared = OutlinedFn.arg_size() == 3;
12182
12183 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12184 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12185 if (HasShared)
12186 OutlinedFn.getArg(2)->setName("data");
12187
12188 // Call to the runtime function for teams in the current function.
12189 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12190 "outlined function.");
12191 Builder.SetInsertPoint(StaleCI);
12192 SmallVector<Value *> Args = {
12193 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12194 if (HasShared)
12195 Args.push_back(StaleCI->getArgOperand(2));
12198 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12199 Args);
12200
12201 Builder.ClearInsertionPoint();
12202 for (Instruction *I : llvm::reverse(ToBeDeleted))
12203 I->eraseFromParent();
12204 };
12205
12206 if (!Config.isTargetDevice())
12207 OI->PostOutlineCB = HostPostOutlineCB;
12208
12209 addOutlineInfo(std::move(OI));
12210
12211 Builder.SetInsertPoint(ExitBB);
12212
12213 return Builder.saveIP();
12214}
12215
12217 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12218 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12219 if (!updateToLocation(Loc))
12220 return InsertPointTy();
12221
12222 BasicBlock *OuterAllocaBB = OuterAllocIP.getBlock();
12223
12224 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12225 BasicBlock *BodyBB =
12226 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12227 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12228 }
12229 BasicBlock *ExitBB =
12230 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12231 BasicBlock *BodyBB =
12232 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12233 BasicBlock *AllocaBB =
12234 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12235
12236 // Generate the body of distribute clause
12237 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12238 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12239 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12240 return Err;
12241
12242 // When using target we use different runtime functions which require a
12243 // callback.
12244 if (Config.isTargetDevice()) {
12245 auto OI = std::make_unique<OutlineInfo>();
12246 OI->OuterAllocBB = OuterAllocIP.getBlock();
12247 OI->EntryBB = AllocaBB;
12248 OI->ExitBB = ExitBB;
12249 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12250 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12251
12252 addOutlineInfo(std::move(OI));
12253 }
12254 Builder.SetInsertPoint(ExitBB);
12255
12256 return Builder.saveIP();
12257}
12258
12261 std::string VarName) {
12262 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12264 Names.size()),
12265 Names);
12266 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12267 M, MapNamesArrayInit->getType(),
12268 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12269 VarName);
12270 return MapNamesArrayGlobal;
12271}
12272
12273// Create all simple and struct types exposed by the runtime and remember
12274// the llvm::PointerTypes of them for easy access later.
12275void OpenMPIRBuilder::initializeTypes(Module &M) {
12276 LLVMContext &Ctx = M.getContext();
12277 StructType *T;
12278 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12279 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12280#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12281#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12282 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12283 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12284#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12285 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12286 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12287#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12288 T = StructType::getTypeByName(Ctx, StructName); \
12289 if (!T) \
12290 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12291 VarName = T; \
12292 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12293#include "llvm/Frontend/OpenMP/OMPKinds.def"
12294}
12295
12298 SmallVectorImpl<BasicBlock *> &BlockVector) {
12300 BlockSet.insert(EntryBB);
12301 BlockSet.insert(ExitBB);
12302
12303 Worklist.push_back(EntryBB);
12304 while (!Worklist.empty()) {
12305 BasicBlock *BB = Worklist.pop_back_val();
12306 BlockVector.push_back(BB);
12307 for (BasicBlock *SuccBB : successors(BB))
12308 if (BlockSet.insert(SuccBB).second)
12309 Worklist.push_back(SuccBB);
12310 }
12311}
12312
12313std::unique_ptr<CodeExtractor>
12315 bool ArgsInZeroAddressSpace,
12316 Twine Suffix) {
12317 return std::make_unique<CodeExtractor>(
12318 Blocks, /* DominatorTree */ nullptr,
12319 /* AggregateArgs */ true,
12320 /* BlockFrequencyInfo */ nullptr,
12321 /* BranchProbabilityInfo */ nullptr,
12322 /* AssumptionCache */ nullptr,
12323 /* AllowVarArgs */ true,
12324 /* AllowAlloca */ true,
12325 /* AllocationBlock*/ OuterAllocBB,
12326 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12327 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12328}
12329
12330std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12331 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12332 return std::make_unique<DeviceSharedMemCodeExtractor>(
12333 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12334 /* AggregateArgs */ true,
12335 /* BlockFrequencyInfo */ nullptr,
12336 /* BranchProbabilityInfo */ nullptr,
12337 /* AssumptionCache */ nullptr,
12338 /* AllowVarArgs */ true,
12339 /* AllowAlloca */ true,
12340 /* AllocationBlock*/ OuterAllocBB,
12341 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12343 : OuterDeallocBBs,
12344 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12345}
12346
12348 uint64_t Size, int32_t Flags,
12350 StringRef Name) {
12351 if (!Config.isGPU()) {
12354 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12355 return;
12356 }
12357 // TODO: Add support for global variables on the device after declare target
12358 // support.
12359 Function *Fn = dyn_cast<Function>(Addr);
12360 if (!Fn)
12361 return;
12362
12363 // Add a function attribute for the kernel.
12364 Fn->addFnAttr("kernel");
12365 if (T.isAMDGCN())
12366 Fn->addFnAttr("uniform-work-group-size");
12367 Fn->addFnAttr(Attribute::MustProgress);
12368}
12369
12370// We only generate metadata for function that contain target regions.
12373
12374 // If there are no entries, we don't need to do anything.
12375 if (OffloadInfoManager.empty())
12376 return;
12377
12378 LLVMContext &C = M.getContext();
12381 16>
12382 OrderedEntries(OffloadInfoManager.size());
12383
12384 // Auxiliary methods to create metadata values and strings.
12385 auto &&GetMDInt = [this](unsigned V) {
12386 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12387 };
12388
12389 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12390
12391 // Create the offloading info metadata node.
12392 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12393 auto &&TargetRegionMetadataEmitter =
12394 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12395 const TargetRegionEntryInfo &EntryInfo,
12397 // Generate metadata for target regions. Each entry of this metadata
12398 // contains:
12399 // - Entry 0 -> Kind of this type of metadata (0).
12400 // - Entry 1 -> Device ID of the file where the entry was identified.
12401 // - Entry 2 -> File ID of the file where the entry was identified.
12402 // - Entry 3 -> Mangled name of the function where the entry was
12403 // identified.
12404 // - Entry 4 -> Line in the file where the entry was identified.
12405 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12406 // - Entry 6 -> Order the entry was created.
12407 // The first element of the metadata node is the kind.
12408 Metadata *Ops[] = {
12409 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12410 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12411 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12412 GetMDInt(E.getOrder())};
12413
12414 // Save this entry in the right position of the ordered entries array.
12415 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12416
12417 // Add metadata to the named metadata node.
12418 MD->addOperand(MDNode::get(C, Ops));
12419 };
12420
12421 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12422
12423 // Create function that emits metadata for each device global variable entry;
12424 auto &&DeviceGlobalVarMetadataEmitter =
12425 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12426 StringRef MangledName,
12428 // Generate metadata for global variables. Each entry of this metadata
12429 // contains:
12430 // - Entry 0 -> Kind of this type of metadata (1).
12431 // - Entry 1 -> Mangled name of the variable.
12432 // - Entry 2 -> Declare target kind.
12433 // - Entry 3 -> Order the entry was created.
12434 // The first element of the metadata node is the kind.
12435 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12436 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12437
12438 // Save this entry in the right position of the ordered entries array.
12439 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12440 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12441
12442 // Add metadata to the named metadata node.
12443 MD->addOperand(MDNode::get(C, Ops));
12444 };
12445
12446 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12447 DeviceGlobalVarMetadataEmitter);
12448
12449 for (const auto &E : OrderedEntries) {
12450 assert(E.first && "All ordered entries must exist!");
12451 if (const auto *CE =
12453 E.first)) {
12454 if (!CE->getID() || !CE->getAddress()) {
12455 // Do not blame the entry if the parent funtion is not emitted.
12456 TargetRegionEntryInfo EntryInfo = E.second;
12457 StringRef FnName = EntryInfo.ParentName;
12458 if (!M.getNamedValue(FnName))
12459 continue;
12460 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12461 continue;
12462 }
12463 createOffloadEntry(CE->getID(), CE->getAddress(),
12464 /*Size=*/0, CE->getFlags(),
12466 } else if (const auto *CE = dyn_cast<
12468 E.first)) {
12471 CE->getFlags());
12472 switch (Flags) {
12475 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12476 continue;
12477 if (!CE->getAddress()) {
12478 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12479 continue;
12480 }
12481 // The vaiable has no definition - no need to add the entry.
12482 if (CE->getVarSize() == 0)
12483 continue;
12484 break;
12486 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12487 (!Config.isTargetDevice() && CE->getAddress())) &&
12488 "Declaret target link address is set.");
12489 if (Config.isTargetDevice())
12490 continue;
12491 if (!CE->getAddress()) {
12493 continue;
12494 }
12495 break;
12498 if (!CE->getAddress()) {
12499 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12500 continue;
12501 }
12502 break;
12503 default:
12504 break;
12505 }
12506
12507 // Hidden or internal symbols on the device are not externally visible.
12508 // We should not attempt to register them by creating an offloading
12509 // entry. Indirect variables are handled separately on the device.
12510 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12511 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12512 (Flags !=
12514 Flags != OffloadEntriesInfoManager::
12515 OMPTargetGlobalVarEntryIndirectVTable))
12516 continue;
12517
12518 // Indirect globals need to use a special name that doesn't match the name
12519 // of the associated host global.
12521 Flags ==
12523 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12524 Flags, CE->getLinkage(), CE->getVarName());
12525 else
12526 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12527 Flags, CE->getLinkage());
12528
12529 } else {
12530 llvm_unreachable("Unsupported entry kind.");
12531 }
12532 }
12533
12534 // Emit requires directive globals to a special entry so the runtime can
12535 // register them when the device image is loaded.
12536 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12537 // entries should be redesigned to better suit this use-case.
12538 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12542 ".requires", /*Size=*/0,
12544 Config.getRequiresFlags());
12545}
12546
12549 unsigned FileID, unsigned Line, unsigned Count) {
12550 raw_svector_ostream OS(Name);
12551 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12552 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12553 if (Count)
12554 OS << "_" << Count;
12555}
12556
12558 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12559 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12561 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12562 EntryInfo.Line, NewCount);
12563}
12564
12567 vfs::FileSystem &VFS,
12568 StringRef ParentName) {
12569 sys::fs::UniqueID ID(0xdeadf17e, 0);
12570 auto FileIDInfo = CallBack();
12571 uint64_t FileID = 0;
12572 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12573 ID = Status->getUniqueID();
12574 FileID = Status->getUniqueID().getFile();
12575 } else {
12576 // If the inode ID could not be determined, create a hash value
12577 // the current file name and use that as an ID.
12578 FileID = hash_value(std::get<0>(FileIDInfo));
12579 }
12580
12581 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12582 std::get<1>(FileIDInfo));
12583}
12584
12586 unsigned Offset = 0;
12587 for (uint64_t Remain =
12588 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12590 !(Remain & 1); Remain = Remain >> 1)
12591 Offset++;
12592 return Offset;
12593}
12594
12597 // Rotate by getFlagMemberOffset() bits.
12598 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12599 << getFlagMemberOffset());
12600}
12601
12604 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12605 // If the entry is PTR_AND_OBJ but has not been marked with the special
12606 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12607 // marked as MEMBER_OF.
12608 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12610 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12613 return;
12614
12615 // Entries with ATTACH are not members-of anything. They are handled
12616 // separately by the runtime after other maps have been handled.
12617 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12619 return;
12620
12621 // Reset the placeholder value to prepare the flag for the assignment of the
12622 // proper MEMBER_OF value.
12623 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12624 Flags |= MemberOfFlag;
12625}
12626
12630 bool IsDeclaration, bool IsExternallyVisible,
12631 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12632 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12633 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12634 std::function<Constant *()> GlobalInitializer,
12635 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12636 // TODO: convert this to utilise the IRBuilder Config rather than
12637 // a passed down argument.
12638 if (OpenMPSIMD)
12639 return nullptr;
12640
12643 CaptureClause ==
12645 Config.hasRequiresUnifiedSharedMemory())) {
12646 SmallString<64> PtrName;
12647 {
12648 raw_svector_ostream OS(PtrName);
12649 OS << MangledName;
12650 if (!IsExternallyVisible)
12651 OS << format("_%x", EntryInfo.FileID);
12652 OS << "_decl_tgt_ref_ptr";
12653 }
12654
12655 Value *Ptr = M.getNamedValue(PtrName);
12656
12657 if (!Ptr) {
12658 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12659 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12660
12661 auto *GV = cast<GlobalVariable>(Ptr);
12662 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12663
12664 if (!Config.isTargetDevice()) {
12665 if (GlobalInitializer)
12666 GV->setInitializer(GlobalInitializer());
12667 else
12668 GV->setInitializer(GlobalValue);
12669 }
12670
12672 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12673 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12674 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12675 }
12676
12677 return cast<Constant>(Ptr);
12678 }
12679
12680 return nullptr;
12681}
12682
12686 bool IsDeclaration, bool IsExternallyVisible,
12687 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12688 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12689 std::vector<Triple> TargetTriple,
12690 std::function<Constant *()> GlobalInitializer,
12691 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12692 Constant *Addr) {
12694 (TargetTriple.empty() && !Config.isTargetDevice()))
12695 return;
12696
12698 StringRef VarName;
12699 int64_t VarSize;
12701
12703 CaptureClause ==
12705 !Config.hasRequiresUnifiedSharedMemory()) {
12707 VarName = MangledName;
12708 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12709
12710 if (!IsDeclaration)
12711 VarSize = divideCeil(
12712 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12713 else
12714 VarSize = 0;
12715 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12716
12717 // This is a workaround carried over from Clang which prevents undesired
12718 // optimisation of internal variables.
12719 if (Config.isTargetDevice() &&
12720 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12721 // Do not create a "ref-variable" if the original is not also available
12722 // on the host.
12723 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12724 return;
12725
12726 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12727
12728 if (!M.getNamedValue(RefName)) {
12729 Constant *AddrRef =
12730 getOrCreateInternalVariable(Addr->getType(), RefName);
12731 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12732 GvAddrRef->setConstant(true);
12733 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12734 GvAddrRef->setInitializer(Addr);
12735 GeneratedRefs.push_back(GvAddrRef);
12736 }
12737 }
12738 } else {
12741 else
12743
12744 if (Config.isTargetDevice()) {
12745 VarName = (Addr) ? Addr->getName() : "";
12746 Addr = nullptr;
12747 } else {
12749 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12750 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12751 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12752 VarName = (Addr) ? Addr->getName() : "";
12753 }
12754 VarSize = M.getDataLayout().getPointerSize();
12756 }
12757
12758 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12759 Flags, Linkage);
12760}
12761
12762/// Loads all the offload entries information from the host IR
12763/// metadata.
12765 // If we are in target mode, load the metadata from the host IR. This code has
12766 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12767
12768 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12769 if (!MD)
12770 return;
12771
12772 for (MDNode *MN : MD->operands()) {
12773 auto &&GetMDInt = [MN](unsigned Idx) {
12774 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12775 return cast<ConstantInt>(V->getValue())->getZExtValue();
12776 };
12777
12778 auto &&GetMDString = [MN](unsigned Idx) {
12779 auto *V = cast<MDString>(MN->getOperand(Idx));
12780 return V->getString();
12781 };
12782
12783 switch (GetMDInt(0)) {
12784 default:
12785 llvm_unreachable("Unexpected metadata!");
12786 break;
12787 case OffloadEntriesInfoManager::OffloadEntryInfo::
12788 OffloadingEntryInfoTargetRegion: {
12789 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12790 /*DeviceID=*/GetMDInt(1),
12791 /*FileID=*/GetMDInt(2),
12792 /*Line=*/GetMDInt(4),
12793 /*Count=*/GetMDInt(5));
12794 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12795 /*Order=*/GetMDInt(6));
12796 break;
12797 }
12798 case OffloadEntriesInfoManager::OffloadEntryInfo::
12799 OffloadingEntryInfoDeviceGlobalVar:
12800 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12801 /*MangledName=*/GetMDString(1),
12803 /*Flags=*/GetMDInt(2)),
12804 /*Order=*/GetMDInt(3));
12805 break;
12806 }
12807 }
12808}
12809
12811 StringRef HostFilePath) {
12812 if (HostFilePath.empty())
12813 return;
12814
12815 auto Buf = VFS.getBufferForFile(HostFilePath);
12816 if (std::error_code Err = Buf.getError()) {
12817 report_fatal_error(("error opening host file from host file path inside of "
12818 "OpenMPIRBuilder: " +
12819 Err.message())
12820 .c_str());
12821 }
12822
12823 LLVMContext Ctx;
12825 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12826 if (std::error_code Err = M.getError()) {
12828 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12829 .c_str());
12830 }
12831
12832 loadOffloadInfoMetadata(*M.get());
12833}
12834
12837 llvm::StringRef Name) {
12838 Builder.restoreIP(Loc.IP);
12839
12840 BasicBlock *CurBB = Builder.GetInsertBlock();
12841 assert(CurBB &&
12842 "expected a valid insertion block for creating an iterator loop");
12843 Function *F = CurBB->getParent();
12844
12845 InsertPointTy SplitIP = Builder.saveIP();
12846 if (SplitIP.getPoint() == CurBB->end())
12847 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12848 SplitIP = InsertPointTy(CurBB, Terminator->getIterator());
12849
12850 BasicBlock *ContBB =
12851 splitBB(SplitIP, /*CreateBranch=*/false,
12852 Builder.getCurrentDebugLocation(), "omp.it.cont");
12853
12854 CanonicalLoopInfo *CLI =
12855 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12856 /*PreInsertBefore=*/ContBB,
12857 /*PostInsertBefore=*/ContBB, Name);
12858
12859 // Enter loop from original block.
12860 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12861
12862 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12863 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12864 T->eraseFromParent();
12865
12866 InsertPointTy BodyIP = CLI->getBodyIP();
12867 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12868 return Err;
12869
12870 // Body must either fallthrough to the latch or branch directly to it.
12871 if (Instruction *BodyTerminator = CLI->getBody()->getTerminatorOrNull()) {
12872 auto *BodyBr = dyn_cast<UncondBrInst>(BodyTerminator);
12873 if (!BodyBr || BodyBr->getSuccessor() != CLI->getLatch()) {
12875 "iterator bodygen must terminate the canonical body with an "
12876 "unconditional branch to the loop latch",
12878 }
12879 } else {
12880 // Ensure we end the loop body by jumping to the latch.
12881 Builder.SetInsertPoint(CLI->getBody());
12882 Builder.CreateBr(CLI->getLatch());
12883 }
12884
12885 // Link After -> ContBB
12886 Builder.SetInsertPoint(CLI->getAfter(), CLI->getAfter()->begin());
12887 if (!CLI->getAfter()->hasTerminator())
12888 Builder.CreateBr(ContBB);
12889
12890 return InsertPointTy{ContBB, ContBB->begin()};
12891}
12892
12893/// Mangle the parameter part of the vector function name according to
12894/// their OpenMP classification. The mangling function is defined in
12895/// section 4.5 of the AAVFABI(2021Q1).
12896static std::string mangleVectorParameters(
12898 SmallString<256> Buffer;
12899 llvm::raw_svector_ostream Out(Buffer);
12900 for (const auto &ParamAttr : ParamAttrs) {
12901 switch (ParamAttr.Kind) {
12903 Out << 'l';
12904 break;
12906 Out << 'R';
12907 break;
12909 Out << 'U';
12910 break;
12912 Out << 'L';
12913 break;
12915 Out << 'u';
12916 break;
12918 Out << 'v';
12919 break;
12920 }
12921 if (ParamAttr.HasVarStride)
12922 Out << "s" << ParamAttr.StrideOrArg;
12923 else if (ParamAttr.Kind ==
12925 ParamAttr.Kind ==
12927 ParamAttr.Kind ==
12929 ParamAttr.Kind ==
12931 // Don't print the step value if it is not present or if it is
12932 // equal to 1.
12933 if (ParamAttr.StrideOrArg < 0)
12934 Out << 'n' << -ParamAttr.StrideOrArg;
12935 else if (ParamAttr.StrideOrArg != 1)
12936 Out << ParamAttr.StrideOrArg;
12937 }
12938
12939 if (!!ParamAttr.Alignment)
12940 Out << 'a' << ParamAttr.Alignment;
12941 }
12942
12943 return std::string(Out.str());
12944}
12945
12947 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
12949 struct ISADataTy {
12950 char ISA;
12951 unsigned VecRegSize;
12952 };
12953 ISADataTy ISAData[] = {
12954 {'b', 128}, // SSE
12955 {'c', 256}, // AVX
12956 {'d', 256}, // AVX2
12957 {'e', 512}, // AVX512
12958 };
12960 switch (Branch) {
12962 Masked.push_back('N');
12963 Masked.push_back('M');
12964 break;
12966 Masked.push_back('N');
12967 break;
12969 Masked.push_back('M');
12970 break;
12971 }
12972 for (char Mask : Masked) {
12973 for (const ISADataTy &Data : ISAData) {
12975 llvm::raw_svector_ostream Out(Buffer);
12976 Out << "_ZGV" << Data.ISA << Mask;
12977 if (!VLENVal) {
12978 assert(NumElts && "Non-zero simdlen/cdtsize expected");
12979 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
12980 } else {
12981 Out << VLENVal;
12982 }
12983 Out << mangleVectorParameters(ParamAttrs);
12984 Out << '_' << Fn->getName();
12985 Fn->addFnAttr(Out.str());
12986 }
12987 }
12988}
12989
12990// Function used to add the attribute. The parameter `VLEN` is templated to
12991// allow the use of `x` when targeting scalable functions for SVE.
12992template <typename T>
12993static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
12994 char ISA, StringRef ParSeq,
12995 StringRef MangledName, bool OutputBecomesInput,
12996 llvm::Function *Fn) {
12997 SmallString<256> Buffer;
12998 llvm::raw_svector_ostream Out(Buffer);
12999 Out << Prefix << ISA << LMask << VLEN;
13000 if (OutputBecomesInput)
13001 Out << 'v';
13002 Out << ParSeq << '_' << MangledName;
13003 Fn->addFnAttr(Out.str());
13004}
13005
13006// Helper function to generate the Advanced SIMD names depending on the value
13007// of the NDS when simdlen is not present.
13008static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
13009 StringRef Prefix, char ISA,
13010 StringRef ParSeq, StringRef MangledName,
13011 bool OutputBecomesInput,
13012 llvm::Function *Fn) {
13013 switch (NDS) {
13014 case 8:
13015 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13016 OutputBecomesInput, Fn);
13017 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
13018 OutputBecomesInput, Fn);
13019 break;
13020 case 16:
13021 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13022 OutputBecomesInput, Fn);
13023 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13024 OutputBecomesInput, Fn);
13025 break;
13026 case 32:
13027 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13028 OutputBecomesInput, Fn);
13029 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13030 OutputBecomesInput, Fn);
13031 break;
13032 case 64:
13033 case 128:
13034 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13035 OutputBecomesInput, Fn);
13036 break;
13037 default:
13038 llvm_unreachable("Scalar type is too wide.");
13039 }
13040}
13041
13042/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
13044 llvm::Function *Fn, unsigned UserVLEN,
13046 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
13047 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
13048
13049 // Sort out parameter sequence.
13050 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
13051 StringRef Prefix = "_ZGV";
13052 StringRef MangledName = Fn->getName();
13053
13054 // Generate simdlen from user input (if any).
13055 if (UserVLEN) {
13056 if (ISA == 's') {
13057 // SVE generates only a masked function.
13058 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13059 OutputBecomesInput, Fn);
13060 return;
13061 }
13062
13063 switch (Branch) {
13065 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13066 OutputBecomesInput, Fn);
13067 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13068 OutputBecomesInput, Fn);
13069 break;
13071 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13072 OutputBecomesInput, Fn);
13073 break;
13075 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13076 OutputBecomesInput, Fn);
13077 break;
13078 }
13079 return;
13080 }
13081
13082 if (ISA == 's') {
13083 // SVE, section 3.4.1, item 1.
13084 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13085 OutputBecomesInput, Fn);
13086 return;
13087 }
13088
13089 switch (Branch) {
13091 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13092 MangledName, OutputBecomesInput, Fn);
13093 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13094 MangledName, OutputBecomesInput, Fn);
13095 break;
13097 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13098 MangledName, OutputBecomesInput, Fn);
13099 break;
13101 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13102 MangledName, OutputBecomesInput, Fn);
13103 break;
13104 }
13105}
13106
13107//===----------------------------------------------------------------------===//
13108// OffloadEntriesInfoManager
13109//===----------------------------------------------------------------------===//
13110
13112 return OffloadEntriesTargetRegion.empty() &&
13113 OffloadEntriesDeviceGlobalVar.empty();
13114}
13115
13116unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13117 const TargetRegionEntryInfo &EntryInfo) const {
13118 auto It = OffloadEntriesTargetRegionCount.find(
13119 getTargetRegionEntryCountKey(EntryInfo));
13120 if (It == OffloadEntriesTargetRegionCount.end())
13121 return 0;
13122 return It->second;
13123}
13124
13125void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13126 const TargetRegionEntryInfo &EntryInfo) {
13127 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13128 EntryInfo.Count + 1;
13129}
13130
13131/// Initialize target region entry.
13133 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13134 OffloadEntriesTargetRegion[EntryInfo] =
13135 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13137 ++OffloadingEntriesNum;
13138}
13139
13141 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13143 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13144
13145 // Update the EntryInfo with the next available count for this location.
13146 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13147
13148 // If we are emitting code for a target, the entry is already initialized,
13149 // only has to be registered.
13150 if (OMPBuilder->Config.isTargetDevice()) {
13151 // This could happen if the device compilation is invoked standalone.
13152 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13153 return;
13154 }
13155 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13156 Entry.setAddress(Addr);
13157 Entry.setID(ID);
13158 Entry.setFlags(Flags);
13159 } else {
13161 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13162 return;
13163 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13164 "Target region entry already registered!");
13165 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13166 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13167 ++OffloadingEntriesNum;
13168 }
13169 incrementTargetRegionEntryInfoCount(EntryInfo);
13170}
13171
13173 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13174
13175 // Update the EntryInfo with the next available count for this location.
13176 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13177
13178 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13179 if (It == OffloadEntriesTargetRegion.end()) {
13180 return false;
13181 }
13182 // Fail if this entry is already registered.
13183 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13184 return false;
13185 return true;
13186}
13187
13189 const OffloadTargetRegionEntryInfoActTy &Action) {
13190 // Scan all target region entries and perform the provided action.
13191 for (const auto &It : OffloadEntriesTargetRegion) {
13192 Action(It.first, It.second);
13193 }
13194}
13195
13197 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13198 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13199 ++OffloadingEntriesNum;
13200}
13201
13203 StringRef VarName, Constant *Addr, int64_t VarSize,
13205 if (OMPBuilder->Config.isTargetDevice()) {
13206 // This could happen if the device compilation is invoked standalone.
13207 if (!hasDeviceGlobalVarEntryInfo(VarName))
13208 return;
13209 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13210 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13211 if (Entry.getVarSize() == 0) {
13212 Entry.setVarSize(VarSize);
13213 Entry.setLinkage(Linkage);
13214 }
13215 return;
13216 }
13217 Entry.setVarSize(VarSize);
13218 Entry.setLinkage(Linkage);
13219 Entry.setAddress(Addr);
13220 } else {
13221 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13222 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13223 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13224 "Entry not initialized!");
13225 if (Entry.getVarSize() == 0) {
13226 Entry.setVarSize(VarSize);
13227 Entry.setLinkage(Linkage);
13228 }
13229 return;
13230 }
13232 Flags ==
13234 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13235 Addr, VarSize, Flags, Linkage,
13236 VarName.str());
13237 else
13238 OffloadEntriesDeviceGlobalVar.try_emplace(
13239 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13240 ++OffloadingEntriesNum;
13241 }
13242}
13243
13246 // Scan all target region entries and perform the provided action.
13247 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13248 Action(E.getKey(), E.getValue());
13249}
13250
13251//===----------------------------------------------------------------------===//
13252// CanonicalLoopInfo
13253//===----------------------------------------------------------------------===//
13254
13255void CanonicalLoopInfo::collectControlBlocks(
13257 // We only count those BBs as control block for which we do not need to
13258 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13259 // flow. For consistency, this also means we do not add the Body block, which
13260 // is just the entry to the body code.
13261 BBs.reserve(BBs.size() + 6);
13262 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13263}
13264
13266 assert(isValid() && "Requires a valid canonical loop");
13267 for (BasicBlock *Pred : predecessors(Header)) {
13268 if (Pred != Latch)
13269 return Pred;
13270 }
13271 llvm_unreachable("Missing preheader");
13272}
13273
13274void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13275 assert(isValid() && "Requires a valid canonical loop");
13276
13277 Instruction *CmpI = &getCond()->front();
13278 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13279 CmpI->setOperand(1, TripCount);
13280
13281#ifndef NDEBUG
13282 assertOK();
13283#endif
13284}
13285
13286void CanonicalLoopInfo::mapIndVar(
13287 llvm::function_ref<Value *(Instruction *)> Updater) {
13288 assert(isValid() && "Requires a valid canonical loop");
13289
13290 Instruction *OldIV = getIndVar();
13291
13292 // Record all uses excluding those introduced by the updater. Uses by the
13293 // CanonicalLoopInfo itself to keep track of the number of iterations are
13294 // excluded.
13295 SmallVector<Use *> ReplacableUses;
13296 for (Use &U : OldIV->uses()) {
13297 auto *User = dyn_cast<Instruction>(U.getUser());
13298 if (!User)
13299 continue;
13300 if (User->getParent() == getCond())
13301 continue;
13302 if (User->getParent() == getLatch())
13303 continue;
13304 ReplacableUses.push_back(&U);
13305 }
13306
13307 // Run the updater that may introduce new uses
13308 Value *NewIV = Updater(OldIV);
13309
13310 // Replace the old uses with the value returned by the updater.
13311 for (Use *U : ReplacableUses)
13312 U->set(NewIV);
13313
13314#ifndef NDEBUG
13315 assertOK();
13316#endif
13317}
13318
13320#ifndef NDEBUG
13321 // No constraints if this object currently does not describe a loop.
13322 if (!isValid())
13323 return;
13324
13325 BasicBlock *Preheader = getPreheader();
13326 BasicBlock *Body = getBody();
13327 BasicBlock *After = getAfter();
13328
13329 // Verify standard control-flow we use for OpenMP loops.
13330 assert(Preheader);
13331 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13332 "Preheader must terminate with unconditional branch");
13333 assert(Preheader->getSingleSuccessor() == Header &&
13334 "Preheader must jump to header");
13335
13336 assert(Header);
13337 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13338 "Header must terminate with unconditional branch");
13339 assert(Header->getSingleSuccessor() == Cond &&
13340 "Header must jump to exiting block");
13341
13342 assert(Cond);
13343 assert(Cond->getSinglePredecessor() == Header &&
13344 "Exiting block only reachable from header");
13345
13346 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13347 "Exiting block must terminate with conditional branch");
13348 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13349 "Exiting block's first successor jump to the body");
13350 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13351 "Exiting block's second successor must exit the loop");
13352
13353 assert(Body);
13354 assert(Body->getSinglePredecessor() == Cond &&
13355 "Body only reachable from exiting block");
13356 assert(!isa<PHINode>(Body->front()));
13357
13358 assert(Latch);
13359 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13360 "Latch must terminate with unconditional branch");
13361 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13362 // TODO: To support simple redirecting of the end of the body code that has
13363 // multiple; introduce another auxiliary basic block like preheader and after.
13364 assert(Latch->getSinglePredecessor() != nullptr);
13365 assert(!isa<PHINode>(Latch->front()));
13366
13367 assert(Exit);
13368 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13369 "Exit block must terminate with unconditional branch");
13370 assert(Exit->getSingleSuccessor() == After &&
13371 "Exit block must jump to after block");
13372
13373 assert(After);
13374 assert(After->getSinglePredecessor() == Exit &&
13375 "After block only reachable from exit block");
13376 assert(After->empty() || !isa<PHINode>(After->front()));
13377
13378 Instruction *IndVar = getIndVar();
13379 assert(IndVar && "Canonical induction variable not found?");
13380 assert(isa<IntegerType>(IndVar->getType()) &&
13381 "Induction variable must be an integer");
13382 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13383 "Induction variable must be a PHI in the loop header");
13384 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13385 assert(
13386 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13387 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13388
13389 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13390 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13391 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13392 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13393 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13394 ->isOne());
13395
13396 Value *TripCount = getTripCount();
13397 assert(TripCount && "Loop trip count not found?");
13398 assert(IndVar->getType() == TripCount->getType() &&
13399 "Trip count and induction variable must have the same type");
13400
13401 auto *CmpI = cast<CmpInst>(&Cond->front());
13402 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13403 "Exit condition must be a signed less-than comparison");
13404 assert(CmpI->getOperand(0) == IndVar &&
13405 "Exit condition must compare the induction variable");
13406 assert(CmpI->getOperand(1) == TripCount &&
13407 "Exit condition must compare with the trip count");
13408#endif
13409}
13410
13412 Header = nullptr;
13413 Cond = nullptr;
13414 Latch = nullptr;
13415 Exit = nullptr;
13416}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
Rewrite undef for PHI
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static cl::opt< ITMode > IT(cl::desc("IT block support"), cl::Hidden, cl::init(DefaultIT), cl::values(clEnumValN(DefaultIT, "arm-default-it", "Generate any type of IT block"), clEnumValN(RestrictedIT, "arm-restrict-it", "Disallow complex IT blocks")))
Expand Atomic instructions
@ ParamAttr
This file contains the simple types necessary to represent the attributes associated with functions a...
static const Function * getParent(const Value *V)
#define X(NUM, ENUM, NAME)
Definition ELF.h:857
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Hexagon Common GEP
Hexagon Hardware Loops
This file provides various utilities for inspecting and working with the control flow graph in LLVM I...
This header defines various interfaces for pass management in LLVM.
iv Induction Variable Users
Definition IVUsers.cpp:48
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
static LVOptions Options
Definition LVOptions.cpp:25
static bool isZero(Value *V, const DataLayout &DL, DominatorTree *DT, AssumptionCache *AC)
Definition Lint.cpp:540
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 Expected< Function * > createOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, StringRef FuncName, SmallVectorImpl< Value * > &Inputs, OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc, OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB, DebugLoc OutlinedFnLoc)
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 void FixupDebugInfoForOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func, DenseMap< Value *, std::tuple< Value *, unsigned > > &ValueReplacementMap)
static OMPScheduleType getOpenMPOrderingScheduleType(OMPScheduleType BaseScheduleType, bool HasOrderedClause)
Adds ordering modifier flags to schedule type.
static OMPScheduleType getOpenMPMonotonicityScheduleType(OMPScheduleType ScheduleType, bool HasSimdModifier, bool HasMonotonic, bool HasNonmonotonic, bool HasOrderedClause)
Adds monotonicity modifier flags to schedule type.
static std::string mangleVectorParameters(ArrayRef< llvm::OpenMPIRBuilder::DeclareSimdAttrTy > ParamAttrs)
Mangle the parameter part of the vector function name according to their OpenMP classification.
static bool isGenericKernel(Function &Fn)
static void workshareLoopTargetCallback(OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident, Function &OutlinedFn, const SmallVector< Instruction *, 4 > &ToBeDeleted, WorksharingLoopType LoopType, bool NoLoop)
static bool isValidWorkshareLoopScheduleType(OMPScheduleType SchedType)
static bool isAtomicableReductionSet(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos)
static llvm::CallInst * emitNoUnwindRuntimeCall(IRBuilder<> &Builder, llvm::FunctionCallee Callee, ArrayRef< llvm::Value * > Args, const llvm::Twine &Name)
static Error populateReductionFunction(Function *ReductionFunc, ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, IRBuilder<> &Builder, ArrayRef< bool > IsByRef, bool IsGPU)
static Function * getFreshReductionFunc(Module &M)
static void raiseUserConstantDataAllocasToEntryBlock(IRBuilderBase &Builder, Function *Function)
static FunctionCallee getKmpcForDynamicNextForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for updating the next loop using OpenMP dynamic scheduling depending...
static bool isConflictIP(IRBuilder<>::InsertPoint IP1, IRBuilder<>::InsertPoint IP2)
Return whether IP1 and IP2 are ambiguous, i.e.
static void checkReductionInfos(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, bool IsGPU)
static Type * getOffloadingArrayType(Value *V)
static OMPScheduleType getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasDistScheduleChunks)
Determine which scheduling algorithm to use, determined from schedule clause arguments.
static OMPScheduleType computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasMonotonicModifier, bool HasNonmonotonicModifier, bool HasOrderedClause, bool HasDistScheduleChunks)
Determine the schedule type using schedule and ordering clause arguments.
static FunctionCallee getKmpcForDynamicInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for initializing loop bounds using OpenMP dynamic scheduling dependi...
static std::optional< omp::OMPTgtExecModeFlags > getTargetKernelExecMode(Function &Kernel)
Given a function, if it represents the entry point of a target kernel, this returns the execution mod...
static StructType * createTaskWithPrivatesTy(OpenMPIRBuilder &OMPIRBuilder, ArrayRef< Value * > OffloadingArraysToPrivatize)
static cl::opt< double > UnrollThresholdFactor("openmp-ir-builder-unroll-threshold-factor", cl::Hidden, cl::desc("Factor for the unroll threshold to account for code " "simplifications still taking place"), cl::init(1.5))
static cl::opt< bool > UseDefaultMaxThreads("openmp-ir-builder-use-default-max-threads", cl::Hidden, cl::desc("Use a default max threads if none is provided."), cl::init(true))
static int32_t computeHeuristicUnrollFactor(CanonicalLoopInfo *CLI)
Heuristically determine the best-performant unroll factor for CLI.
static 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, DebugLoc OutlinedFnLoc)
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 void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value, bool Min)
static OpenMPIRBuilder::InsertPointTy getInsertPointAfterInstr(Instruction *I)
static void redirectAllPredecessorsTo(BasicBlock *OldTarget, BasicBlock *NewTarget, DebugLoc DL)
Redirect all edges that branch to OldTarget to NewTarget.
static void hoistNonEntryAllocasToEntryBlock(llvm::BasicBlock &Block)
static std::unique_ptr< TargetMachine > createTargetMachine(Function *F, CodeGenOptLevel OptLevel)
Create the TargetMachine object to query the backend for optimization preferences.
static FunctionCallee getKmpcForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup, LoopInfo &LI)
Attach llvm.access.group metadata to the memref instructions of Block.
static void addBasicBlockMetadata(BasicBlock *BB, ArrayRef< Metadata * > Properties)
Attach metadata Properties to the basic block described by BB.
static void restoreIPandDebugLoc(llvm::IRBuilderBase &Builder, llvm::IRBuilderBase::InsertPoint IP)
This is a wrapper over IRBuilderBase::restoreIP that also restores a current debug location when the ...
static LoadInst * loadSharedDataFromTaskDescriptor(OpenMPIRBuilder &OMPIRBuilder, IRBuilderBase &Builder, Value *TaskWithPrivates, Type *TaskWithPrivatesTy)
Given a task descriptor, TaskWithPrivates, return the pointer to the block of pointers containing sha...
static cl::opt< bool > OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden, cl::desc("Use optimistic attributes describing " "'as-if' properties of runtime calls."), cl::init(false))
static bool hasGridValue(const Triple &T)
static FunctionCallee getKmpcForStaticLoopForType(Type *Ty, OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType)
static const omp::GV & getGridValue(const Triple &T, Function *Kernel)
static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask, StringRef Prefix, char ISA, StringRef ParSeq, StringRef MangledName, bool OutputBecomesInput, llvm::Function *Fn)
static Function * emitTargetTaskProxyFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI, StructType *PrivatesTy, StructType *TaskWithPrivatesTy, const size_t NumOffloadingArrays, const int SharedArgsOperandNo)
Create an entry point for a target task with the following.
static void addLoopMetadata(CanonicalLoopInfo *Loop, ArrayRef< Metadata * > Properties)
Attach loop metadata Properties to the loop described by Loop.
static AtomicOrdering TransformReleaseAcquireRelease(AtomicOrdering AO)
static void removeUnusedBlocksFromParent(ArrayRef< BasicBlock * > BBs)
static void targetParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition, Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr, Value *ThreadID, const SmallVector< Instruction *, 4 > &ToBeDeleted)
static void hostParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, Value *Ident, Value *IfCondition, Instruction *PrivTID, AllocaInst *PrivTIDAddr, const SmallVector< Instruction *, 4 > &ToBeDeleted)
#define P(N)
FunctionAnalysisManager FAM
Function * Fun
This file defines the Pass Instrumentation classes that provide instrumentation points into the pass ...
const SmallVectorImpl< MachineOperand > & Cond
Remove Loads Into Fake Uses
static bool isValid(const char C)
Returns true if C is a valid mangled character: <0-9a-zA-Z_>.
SmallPtrSet< BasicBlock *, 0 > BlockSet
This file implements the SmallBitVector class.
This file defines the SmallSet class.
This file defines less commonly used SmallVector utilities.
This file contains some functions that are useful when dealing with strings.
#define LLVM_DEBUG(...)
Definition Debug.h:119
static SymbolRef::Type getType(const Symbol *Sym)
Definition TapiFile.cpp:39
Defines the virtual file system interface vfs::FileSystem.
Value * RHS
Value * LHS
static cl::opt< unsigned > MaxThreads("xcore-max-threads", cl::Optional, cl::desc("Maximum number of threads (for emulation thread-local storage)"), cl::Hidden, cl::value_desc("number"), cl::init(8))
static const uint32_t IV[8]
Definition blake3_impl.h:83
The Input class is used to parse a yaml document into in-memory structs and vectors.
Class for arbitrary precision integers.
Definition APInt.h:78
static APInt getSignedMaxValue(unsigned numBits)
Gets maximum signed value of APInt for a specific bit width.
Definition APInt.h:206
An arbitrary precision integer that knows its signedness.
Definition APSInt.h:24
static APSInt getUnsigned(uint64_t X)
Definition APSInt.h:349
This class represents a conversion between pointers from one address space to another.
an instruction to allocate memory on the stack
Align getAlign() const
Return the alignment of the memory that is being allocated by the instruction.
PointerType * getType() const
Overload to return most specific pointer type.
Type * getAllocatedType() const
Return the type that is being allocated by the instruction.
unsigned getAddressSpace() const
Return the address space for the allocation.
LLVM_ABI std::optional< TypeSize > getAllocationSize(const DataLayout &DL) const
Get allocation size in bytes.
LLVM_ABI bool isArrayAllocation() const
Return true if there is an allocation size parameter to the allocation instruction that is not 1.
void setAlignment(Align Align)
const Value * getArraySize() const
Get the number of elements allocated.
bool registerPass(PassBuilderT &&PassBuilder)
Register an analysis pass with the manager.
This class represents an incoming formal argument to a Function.
Definition Argument.h:32
unsigned getArgNo() const
Return the index of this formal argument in its containing function.
Definition Argument.h:50
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
iterator end() const
Definition ArrayRef.h:130
size_t size() const
Get the array size.
Definition ArrayRef.h:141
bool empty() const
Check if the array is empty.
Definition ArrayRef.h:136
Class to represent array types.
static LLVM_ABI ArrayType * get(Type *ElementType, uint64_t NumElements)
This static method is the primary way to construct an ArrayType.
A function analysis which provides an AssumptionCache.
LLVM_ABI AssumptionCache run(Function &F, FunctionAnalysisManager &)
A cache of @llvm.assume calls within a function.
An instruction that atomically checks whether a specified value is in a memory location,...
void setWeak(bool IsWeak)
static AtomicOrdering getStrongestFailureOrdering(AtomicOrdering SuccessOrdering)
Returns the strongest permitted ordering on failure, given the desired ordering on success.
LLVM_ABI std::pair< LoadInst *, AllocaInst * > EmitAtomicLoadLibcall(AtomicOrdering AO)
Definition Atomic.cpp:109
LLVM_ABI void EmitAtomicStoreLibcall(AtomicOrdering AO, Value *Source)
Definition Atomic.cpp:150
an instruction that atomically reads a memory location, combines it with another value,...
BinOp
This enumeration lists the possible modifications atomicrmw can make.
@ Add
*p = old + v
@ FAdd
*p = old + v
@ USubCond
Subtract only if no unsigned overflow.
@ FMinimum
*p = minimum(old, v) minimum matches the behavior of llvm.minimum.
@ Min
*p = old <signed v ? old : v
@ Sub
*p = old - v
@ And
*p = old & v
@ Xor
*p = old ^ v
@ USubSat
*p = usub.sat(old, v) usub.sat matches the behavior of llvm.usub.sat.
@ FMaximum
*p = maximum(old, v) maximum matches the behavior of llvm.maximum.
@ FSub
*p = old - v
@ UIncWrap
Increment one up to a maximum value.
@ Max
*p = old >signed v ? old : v
@ UMin
*p = old <unsigned v ? old : v
@ FMin
*p = minnum(old, v) minnum matches the behavior of llvm.minnum.
@ UMax
*p = old >unsigned v ? old : v
@ FMaximumNum
*p = maximumnum(old, v) maximumnum matches the behavior of llvm.maximumnum.
@ FMax
*p = maxnum(old, v) maxnum matches the behavior of llvm.maxnum.
@ UDecWrap
Decrement one until a minimum value or zero.
@ FMinimumNum
*p = minimumnum(old, v) minimumnum matches the behavior of llvm.minimumnum.
@ Nand
*p = ~(old & v)
This class holds the attributes for a particular argument, parameter, function, or return value.
Definition Attributes.h:410
LLVM_ABI AttributeSet addAttributes(LLVMContext &C, AttributeSet AS) const
Add attributes to the attribute set.
LLVM_ABI AttributeSet addAttribute(LLVMContext &C, Attribute::AttrKind Kind) const
Add an argument attribute.
static LLVM_ABI Attribute getWithAlignment(LLVMContext &Context, Align Alignment)
Return a uniquified Attribute object that has the specific alignment set.
LLVM Basic Block Representation.
Definition BasicBlock.h:62
LLVM_ABI void replaceSuccessorsPhiUsesWith(BasicBlock *Old, BasicBlock *New)
Update all phi nodes in this basic block's successors to refer to basic block New instead of basic bl...
iterator end()
Definition BasicBlock.h:459
iterator begin()
Instruction iterator methods.
Definition BasicBlock.h:446
LLVM_ABI const_iterator getFirstInsertionPt() const
Returns an iterator to the first instruction in this block that is suitable for inserting a non-PHI i...
LLVM_ABI BasicBlock * splitBasicBlock(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction.
const Function * getParent() const
Return the enclosing method, or null if none.
Definition BasicBlock.h:213
reverse_iterator rbegin()
Definition BasicBlock.h:462
bool hasTerminator() const LLVM_READONLY
Returns whether the block has a terminator.
Definition BasicBlock.h:232
bool empty() const
Definition BasicBlock.h:468
const Instruction & back() const
Definition BasicBlock.h:471
LLVM_ABI BasicBlock * splitBasicBlockBefore(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction and insert the new basic blo...
LLVM_ABI InstListType::const_iterator getFirstNonPHIIt() const
Returns an iterator to the first instruction in this block that is not a PHINode instruction.
LLVM_ABI void insertDbgRecordBefore(DbgRecord *DR, InstListType::iterator Here)
Insert a DbgRecord into a block at the position given by Here.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
LLVM_ABI InstListType::const_iterator getFirstNonPHIOrDbg(bool SkipPseudoOp=true) const
Returns a pointer to the first instruction in this block that is not a PHINode or a debug intrinsic,...
LLVM_ABI const BasicBlock * getUniqueSuccessor() const
Return the successor of this block if it has a unique successor.
LLVM_ABI const BasicBlock * getSinglePredecessor() const
Return the predecessor of this block if it has a single predecessor block.
const Instruction & front() const
Definition BasicBlock.h:469
InstListType::reverse_iterator reverse_iterator
Definition BasicBlock.h:172
LLVM_ABI const BasicBlock * getUniquePredecessor() const
Return the predecessor of this block if it has a unique predecessor block.
const Instruction * getTerminatorOrNull() const LLVM_READONLY
Returns the terminator instruction if the block is well formed or null if the block is not well forme...
Definition BasicBlock.h:248
LLVM_ABI const BasicBlock * getSingleSuccessor() const
Return the successor of this block if it has a single successor.
LLVM_ABI SymbolTableList< BasicBlock >::iterator eraseFromParent()
Unlink 'this' from the containing function and delete it.
reverse_iterator rend()
Definition BasicBlock.h:464
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
LLVM_ABI LLVMContext & getContext() const
Get the context in which this basic block lives.
void moveBefore(BasicBlock *MovePos)
Unlink this basic block from its current function and insert it into the function that MovePos lives ...
Definition BasicBlock.h:373
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction; assumes that the block is well-formed.
Definition BasicBlock.h:237
void splice(BasicBlock::iterator ToIt, BasicBlock *FromBB)
Transfer all instructions from FromBB to this basic block at ToIt.
Definition BasicBlock.h:644
LLVM_ABI void removePredecessor(BasicBlock *Pred, bool KeepOneInputPHIs=false)
Update PHI nodes in this BasicBlock before removal of predecessor Pred.
void setDoesNotThrow()
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
Value * getArgOperand(unsigned i) const
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
unsigned arg_size() const
This class represents a function call, abstracting a target machine's calling convention.
Class to represented the control flow structure of an OpenMP canonical loop.
Value * getTripCount() const
Returns the llvm::Value containing the number of loop iterations.
BasicBlock * getHeader() const
The header is the entry for each iteration.
LLVM_ABI void assertOK() const
Consistency self-check.
Type * getIndVarType() const
Return the type of the induction variable (and the trip count).
BasicBlock * getBody() const
The body block is the single entry for a loop iteration and not controlled by CanonicalLoopInfo.
bool isValid() const
Returns whether this object currently represents the IR of a loop.
void setLastIter(Value *IterVar)
Sets the last iteration variable for this loop.
OpenMPIRBuilder::InsertPointTy getAfterIP() const
Return the insertion point for user code after the loop.
OpenMPIRBuilder::InsertPointTy getBodyIP() const
Return the insertion point for user code in the body.
BasicBlock * getAfter() const
The after block is intended for clean-up code such as lifetime end markers.
Function * getFunction() const
LLVM_ABI void invalidate()
Invalidate this loop.
BasicBlock * getLatch() const
Reaching the latch indicates the end of the loop body code.
OpenMPIRBuilder::InsertPointTy getPreheaderIP() const
Return the insertion point for user code before the loop.
BasicBlock * getCond() const
The condition block computes whether there is another loop iteration.
BasicBlock * getExit() const
Reaching the exit indicates no more iterations are being executed.
LLVM_ABI BasicBlock * getPreheader() const
The preheader ensures that there is only a single edge entering the loop.
Instruction * getIndVar() const
Returns the instruction representing the current logical induction variable.
Predicate
This enumeration lists the possible predicates for CmpInst subclasses.
Definition InstrTypes.h:740
@ ICMP_SLT
signed less than
Definition InstrTypes.h:769
@ ICMP_SLE
signed less or equal
Definition InstrTypes.h:770
@ FCMP_OLT
0 1 0 0 True if ordered and less than
Definition InstrTypes.h:746
@ FCMP_OGT
0 0 1 0 True if ordered and greater than
Definition InstrTypes.h:744
@ ICMP_UGT
unsigned greater than
Definition InstrTypes.h:763
@ ICMP_SGT
signed greater than
Definition InstrTypes.h:767
@ ICMP_ULT
unsigned less than
Definition InstrTypes.h:765
@ ICMP_NE
not equal
Definition InstrTypes.h:762
@ ICMP_ULE
unsigned less or equal
Definition InstrTypes.h:766
A cache for the CodeExtractor analysis.
Utility class for extracting code into a new function.
static LLVM_ABI Constant * get(ArrayType *T, ArrayRef< Constant * > V)
static ConstantAsMetadata * get(Constant *C)
Definition Metadata.h:537
static Constant * get(LLVMContext &Context, ArrayRef< ElementTy > Elts)
get() constructor - Return a constant with array type with an element count and element type matching...
Definition Constants.h:878
static LLVM_ABI Constant * getString(LLVMContext &Context, StringRef Initializer, bool AddNull=true, bool ByteString=false)
This method constructs a CDS and initializes it with a text string.
static LLVM_ABI Constant * getPointerCast(Constant *C, Type *Ty)
Create a BitCast, AddrSpaceCast, or a PtrToInt cast constant expression.
static LLVM_ABI Constant * getTruncOrBitCast(Constant *C, Type *Ty)
static LLVM_ABI Constant * getPointerBitCastOrAddrSpaceCast(Constant *C, Type *Ty)
Create a BitCast or AddrSpaceCast for a pointer type depending on the address space.
static LLVM_ABI Constant * getSizeOf(Type *Ty)
getSizeOf constant expr - computes the (alloc) size of a type (in address-units, not bits) in a targe...
static LLVM_ABI Constant * getAddrSpaceCast(Constant *C, Type *Ty, bool OnlyIfReduced=false)
static LLVM_ABI ConstantFP * getZero(Type *Ty, bool Negative=false)
This is the shared class of boolean and integer constants.
Definition Constants.h:87
static ConstantInt * getSigned(IntegerType *Ty, int64_t V, bool ImplicitTrunc=false)
Return a ConstantInt with the specified value for the specified type.
Definition Constants.h:135
static LLVM_ABI ConstantPointerNull * get(PointerType *T)
Static factory methods - Return objects of the specified value.
static LLVM_ABI Constant * get(StructType *T, ArrayRef< Constant * > V)
This is an important base class in LLVM.
Definition Constant.h:43
static LLVM_ABI Constant * getAllOnesValue(Type *Ty)
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
DILocalScope * getScope() const
Get the local scope for this variable.
DINodeArray getAnnotations() const
DIFile * getFile() const
Subprogram description. Uses SubclassData1.
Base class for types.
uint32_t getAlignInBits() const
DIFile * getFile() const
DIType * getType() const
unsigned getLine() const
StringRef getName() const
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
TypeSize getTypeStoreSize(Type *Ty) const
Returns the maximum number of bytes that may be overwritten by storing the specified type.
Definition DataLayout.h:579
Record of a variable value-assignment, aka a non instruction representation of the dbg....
A debug info location.
Definition DebugLoc.h:126
Analysis pass which computes a DominatorTree.
Definition Dominators.h:241
LLVM_ABI DominatorTree run(Function &F, FunctionAnalysisManager &)
Run the analysis pass over a function and produce a dominator tree.
bool properlyDominates(const DomTreeNodeBase< NodeT > *A, const DomTreeNodeBase< NodeT > *B) const
properlyDominates - Returns true iff A dominates B and A != B.
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Definition Dominators.h:122
Represents either an error or a value T.
Definition ErrorOr.h:56
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
reference get()
Returns a reference to the stored T value.
Definition Error.h:582
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
Class to represent function types.
Type * getParamType(unsigned i) const
Parameter type accessors.
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
void addFnAttr(Attribute::AttrKind Kind)
Add function attributes to this function.
Definition Function.cpp:640
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition Function.h:169
const BasicBlock & getEntryBlock() const
Definition Function.h:794
Argument * arg_iterator
Definition Function.h:73
bool empty() const
Definition Function.h:844
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Definition Function.h:212
void removeFromParent()
removeFromParent - This method unlinks 'this' from the containing module, but does not delete it.
Definition Function.cpp:447
const DataLayout & getDataLayout() const
Get the data layout of the module this function belongs to.
Definition Function.cpp:360
Attribute getFnAttribute(Attribute::AttrKind Kind) const
Return the attribute for the given attribute kind.
Definition Function.cpp:765
DISubprogram * getSubprogram() const
Get the attached subprogram.
AttributeList getAttributes() const
Return the attribute list for this Function.
Definition Function.h:329
const Function & getFunction() const
Definition Function.h:167
iterator begin()
Definition Function.h:838
arg_iterator arg_begin()
Definition Function.h:853
void setAttributes(AttributeList Attrs)
Set the attribute list for this Function.
Definition Function.h:332
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:356
void addParamAttr(unsigned ArgNo, Attribute::AttrKind Kind)
adds the attribute to the list of attributes for the given arg.
Definition Function.cpp:668
Function::iterator insert(Function::iterator Position, BasicBlock *BB)
Insert BB in the basic block list at Position.
Definition Function.h:740
size_t arg_size() const
Definition Function.h:886
Type * getReturnType() const
Returns the type of the ret val.
Definition Function.h:217
iterator end()
Definition Function.h:840
void setCallingConv(CallingConv::ID CC)
Definition Function.h:277
Argument * getArg(unsigned i) const
Definition Function.h:871
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:2908
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.
user_iterator user_begin()
user_iterator user_end()
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:338
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
An instruction for reading from memory.
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this load instruction.
Align getAlign() const
Return the alignment of the access that is being performed.
Analysis pass that exposes the LoopInfo for a function.
Definition LoopInfo.h:594
LLVM_ABI LoopInfo run(Function &F, FunctionAnalysisManager &AM)
ArrayRef< BlockT * > getBlocks() const
Get a list of the basic blocks which make up this loop.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
This class represents a loop nest and can be used to query its properties.
Represents a single loop in the control flow graph.
Definition LoopInfo.h:40
LLVM_ABI MDNode * createCallbackEncoding(unsigned CalleeArgNo, ArrayRef< int > Arguments, bool VarArgsArePassed)
Return metadata describing a callback (see llvm::AbstractCallSite).
Metadata node.
Definition Metadata.h:1069
LLVM_ABI void replaceOperandWith(unsigned I, Metadata *New)
Replace a specific operand.
static MDTuple * getDistinct(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1575
ArrayRef< MDOperand > operands() const
Definition Metadata.h:1424
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1567
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Definition Metadata.cpp:611
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:68
LLVMContext & getContext() const
Get the global data context.
Definition Module.h:332
const DataLayout & getDataLayout() const
Get the data layout for the module's target platform.
Definition Module.h:325
A tuple of MDNodes.
Definition Metadata.h:1755
iterator_range< op_iterator > operands()
Definition Metadata.h:1851
LLVM_ABI void addOperand(MDNode *M)
Class that manages information about offload code regions and data.
function_ref< void(StringRef, const OffloadEntryInfoDeviceGlobalVar &)> OffloadDeviceGlobalVarEntryInfoActTy
Applies action Action on all registered entries.
OMPTargetDeviceClauseKind
Kind of device clause for declare target variables and functions NOTE: Currently not used as a part o...
@ OMPTargetDeviceClauseAny
The target is marked for all devices.
LLVM_ABI void registerDeviceGlobalVarEntryInfo(StringRef VarName, Constant *Addr, int64_t VarSize, OMPTargetGlobalVarEntryKind Flags, GlobalValue::LinkageTypes Linkage)
Register device global variable entry.
LLVM_ABI void initializeDeviceGlobalVarEntryInfo(StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order)
Initialize device global variable entry.
LLVM_ABI void actOnDeviceGlobalVarEntriesInfo(const OffloadDeviceGlobalVarEntryInfoActTy &Action)
OMPTargetRegionEntryKind
Kind of the target registry entry.
@ OMPTargetRegionEntryTargetRegion
Mark the entry as target region.
LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, const TargetRegionEntryInfo &EntryInfo)
LLVM_ABI bool hasTargetRegionEntryInfo(TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId=false) const
Return true if a target region entry with the provided information exists.
LLVM_ABI void registerTargetRegionEntryInfo(TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID, OMPTargetRegionEntryKind Flags)
Register target region entry.
LLVM_ABI void actOnTargetRegionEntriesInfo(const OffloadTargetRegionEntryInfoActTy &Action)
LLVM_ABI void initializeTargetRegionEntryInfo(const TargetRegionEntryInfo &EntryInfo, unsigned Order)
Initialize target region entry.
OMPTargetGlobalVarEntryKind
Kind of the global variable entry..
@ OMPTargetGlobalVarEntryEnter
Mark the entry as a declare target enter.
@ OMPTargetGlobalRegisterRequires
Mark the entry as a register requires global.
@ OMPTargetGlobalVarEntryIndirect
Mark the entry as a declare target indirect global.
@ OMPTargetGlobalVarEntryLink
Mark the entry as a to declare target link.
@ OMPTargetGlobalVarEntryTo
Mark the entry as a to declare target.
@ OMPTargetGlobalVarEntryIndirectVTable
Mark the entry as a declare target indirect vtable.
function_ref< void(const TargetRegionEntryInfo &EntryInfo, const OffloadEntryInfoTargetRegion &)> OffloadTargetRegionEntryInfoActTy
brief Applies action Action on all registered entries.
bool hasDeviceGlobalVarEntryInfo(StringRef VarName) const
Checks if the variable with the given name has been registered already.
LLVM_ABI bool empty() const
Return true if a there are no entries defined.
std::optional< bool > IsTargetDevice
Flag to define whether to generate code for the role of the OpenMP host (if set to false) or device (...
std::optional< bool > IsGPU
Flag for specifying if the compilation is done for an accelerator.
LLVM_ABI int64_t getRequiresFlags() const
Returns requires directive clauses as flags compatible with those expected by libomptarget.
std::optional< bool > OpenMPOffloadMandatory
Flag for specifying if offloading is mandatory.
LLVM_ABI void setHasRequiresReverseOffload(bool Value)
LLVM_ABI bool hasRequiresUnifiedSharedMemory() const
LLVM_ABI void setHasRequiresUnifiedSharedMemory(bool Value)
unsigned getDefaultTargetAS() const
LLVM_ABI bool hasRequiresDynamicAllocators() const
LLVM_ABI void setHasRequiresUnifiedAddress(bool Value)
LLVM_ABI void setHasRequiresDynamicAllocators(bool Value)
LLVM_ABI bool hasRequiresReverseOffload() const
LLVM_ABI bool hasRequiresUnifiedAddress() const
Struct that keeps the information that should be kept throughout a 'target data' region.
An interface to create LLVM-IR for OpenMP directives.
LLVM_ABI InsertPointOrErrorTy createOrderedThreadsSimd(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsThreads)
Generator for 'omp ordered [threads | simd]'.
LLVM_ABI void emitAArch64DeclareSimdFunction(llvm::Function *Fn, unsigned VLENVal, llvm::ArrayRef< DeclareSimdAttrTy > ParamAttrs, DeclareSimdBranch Branch, char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput)
Emit AArch64 vector-function ABI attributes for a declare simd function.
LLVM_ABI Constant * getOrCreateIdent(Constant *SrcLocStr, uint32_t SrcLocStrSize, omp::IdentFlag Flags=omp::IdentFlag(0), unsigned Reserve2Flags=0)
Return an ident_t* encoding the source location SrcLocStr and Flags.
LLVM_ABI void registerDeclareTargetGlobalReplacement(GlobalValue *Original, GlobalValue *Replacement)
Register a module-scope replacement of a declare target global variable.
LLVM_ABI FunctionCallee getOrCreateRuntimeFunction(Module &M, omp::RuntimeFunction FnID)
Return the function declaration for the runtime function with FnID.
LLVM_ABI InsertPointOrErrorTy createCancel(const LocationDescription &Loc, Value *IfCondition, omp::Directive CanceledDirective)
Generator for 'omp cancel'.
std::function< Expected< Function * >(StringRef FunctionName)> FunctionGenCallback
Functions used to generate a function with the given name.
LLVM_ABI CallInst * createOMPAllocShared(const LocationDescription &Loc, Value *Size, const Twine &Name=Twine(""))
Create a runtime call for kmpc_alloc_shared.
ReductionGenCBKind
Enum class for the RedctionGen CallBack type to be used.
LLVM_ABI CanonicalLoopInfo * collapseLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops, InsertPointTy ComputeIP)
Collapse a loop nest into a single loop.
LLVM_ABI void createTaskyield(const LocationDescription &Loc)
Generator for 'omp taskyield'.
std::function< Error(InsertPointTy CodeGenIP)> FinalizeCallbackTy
Callback type for variable finalization (think destructors).
LLVM_ABI void emitBranch(BasicBlock *Target)
LLVM_ABI Error emitCancelationCheckImpl(Value *CancelFlag, omp::Directive CanceledDirective)
Generate control flow and cleanup for cancellation.
static LLVM_ABI void writeThreadBoundsForKernel(const Triple &T, Function &Kernel, int32_t LB, int32_t UB)
LLVM_ABI void emitTaskwaitImpl(const LocationDescription &Loc)
Generate a taskwait runtime call.
LLVM_ABI Constant * registerTargetRegionFunction(TargetRegionEntryInfo &EntryInfo, Function *OutlinedFunction, StringRef EntryFnName, StringRef EntryFnIDName)
Registers the given function and sets up the attribtues of the function Returns the FunctionID.
LLVM_ABI GlobalVariable * emitKernelExecutionMode(StringRef KernelName, omp::OMPTgtExecModeFlags Mode)
Emit the kernel execution mode.
LLVM_ABI void initialize()
Initialize the internal state, this will put structures types and potentially other helpers into the ...
LLVM_ABI InsertPointTy createAtomicCompare(const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V, AtomicOpValue &R, Value *E, Value *D, AtomicOrdering AO, omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate, bool IsFailOnly, bool IsWeak=false)
LLVM_ABI InsertPointTy createAtomicWrite(const LocationDescription &Loc, AtomicOpValue &X, Value *Expr, AtomicOrdering AO, InsertPointTy AllocaIP)
Emit atomic write for : X = Expr — Only Scalar data types.
LLVM_ABI void loadOffloadInfoMetadata(Module &M)
Loads all the offload entries information from the host IR metadata.
function_ref< MapInfosTy &(InsertPointTy CodeGenIP)> GenMapInfoCallbackTy
Callback type for creating the map infos for the kernel parameters.
LLVM_ABI Error emitOffloadingArrays(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo, TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous=false, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr)
Emit the arrays used to pass the captures and map information to the offloading runtime library.
LLVM_ABI void unrollLoopFull(DebugLoc DL, CanonicalLoopInfo *Loop)
Fully unroll a loop.
function_ref< Error(InsertPointTy CodeGenIP, Value *IndVar)> LoopBodyGenCallbackTy
Callback type for loop body code generation.
LLVM_ABI InsertPointOrErrorTy emitScanReduction(const LocationDescription &Loc, ArrayRef< llvm::OpenMPIRBuilder::ReductionInfo > ReductionInfos, ScanInfo *ScanRedInfo)
This function performs the scan reduction of the values updated in the input phase.
LLVM_ABI void emitFlush(const LocationDescription &Loc)
Generate a flush runtime call.
LLVM_ABI InsertPointOrErrorTy createScope(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsNowait)
Generator for 'omp scope'.
static LLVM_ABI std::pair< int32_t, int32_t > readThreadBoundsForKernel(const Triple &T, Function &Kernel)
}
OpenMPIRBuilderConfig Config
The OpenMPIRBuilder Configuration.
LLVM_ABI CallInst * createOMPInteropDestroy(const LocationDescription &Loc, Value *InteropVar, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_destroy.
LLVM_ABI void emitUsed(StringRef Name, ArrayRef< llvm::WeakTrackingVH > List)
Emit the llvm.used metadata.
LLVM_ABI InsertPointOrErrorTy createSingle(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef< llvm::Value * > CPVars={}, ArrayRef< llvm::Function * > CPFuncs={})
Generator for 'omp single'.
LLVM_ABI InsertPointOrErrorTy createTeams(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower=nullptr, Value *NumTeamsUpper=nullptr, Value *ThreadLimit=nullptr, Value *IfExpr=nullptr)
Generator for #omp teams
std::forward_list< CanonicalLoopInfo > LoopInfos
Collection of owned canonical loop objects that eventually need to be free'd.
LLVM_ABI llvm::StructType * getKmpTaskAffinityInfoTy()
Return the LLVM struct type matching runtime kmp_task_affinity_info_t.
LLVM_ABI std::string createPlatformSpecificName(ArrayRef< StringRef > Parts) const
Get the create a name using the platform specific separators.
LLVM_ABI FunctionCallee createDispatchNextFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_next_* runtime function for the specified size IVSize and sign IVSigned.
static LLVM_ABI void getKernelArgsVector(TargetKernelArgs &KernelArgs, IRBuilderBase &Builder, SmallVector< Value * > &ArgsVector)
Create the kernel args vector used by emitTargetKernel.
LLVM_ABI void unrollLoopHeuristic(DebugLoc DL, CanonicalLoopInfo *Loop)
Fully or partially unroll a loop.
LLVM_ABI omp::OpenMPOffloadMappingFlags getMemberOfFlag(unsigned Position)
Get OMP_MAP_MEMBER_OF flag with extra bits reserved based on the position given.
LLVM_ABI void addAttributes(omp::RuntimeFunction FnID, Function &Fn)
Add attributes known for FnID to Fn.
Module & M
The underlying LLVM-IR module.
StringMap< Constant * > SrcLocStrMap
Map to remember source location strings.
LLVM_ABI void createMapperAllocas(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumOperands, struct MapperAllocas &MapperAllocas)
Create the allocas instruction used in call to mapper functions.
SmallVector< DeclareTargetGlobalReplacement, 8 > DeclareTargetGlobalReplacements
Collection of declare target globals to rewrite uses of during device module finalizaiton.
LLVM_ABI Constant * getOrCreateSrcLocStr(StringRef LocStr, uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the source location LocStr.
LLVM_ABI Error emitTargetRegionFunction(TargetRegionEntryInfo &EntryInfo, FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry, Function *&OutlinedFn, Constant *&OutlinedFnID)
Create a unique name for the entry function using the source location information of the current targ...
LLVM_ABI InsertPointOrErrorTy createIteratorLoop(LocationDescription Loc, llvm::Value *TripCount, IteratorBodyGenTy BodyGen, llvm::StringRef Name="iterator")
Create a canonical iterator loop at the current insertion point.
LLVM_ABI Expected< SmallVector< llvm::CanonicalLoopInfo * > > createCanonicalScanLoops(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo)
Generator for the control flow structure of an OpenMP canonical loops if the parent directive has an ...
LLVM_ABI FunctionCallee createDispatchFiniFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_fini_* runtime function for the specified size IVSize and sign IVSigned.
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> TargetBodyGenCallbackTy
LLVM_ABI void unrollLoopPartial(DebugLoc DL, CanonicalLoopInfo *Loop, int32_t Factor, CanonicalLoopInfo **UnrolledCLI)
Partially unroll a loop.
function_ref< Error(Value *DeviceID, Value *RTLoc, IRBuilderBase::InsertPoint TargetTaskAllocaIP)> TargetTaskBodyCallbackTy
Callback type for generating the bodies of device directives that require outer target tasks (e....
Expected< MapInfosTy & > MapInfosOrErrorTy
bool HandleFPNegZero
Emit atomic compare for constructs: — Only scalar data types cond-expr-stmt: x = x ordop expr ?
LLVM_ABI void emitTaskyieldImpl(const LocationDescription &Loc)
Generate a taskyield runtime call.
LLVM_ABI void emitMapperCall(const LocationDescription &Loc, Function *MapperFunc, Value *SrcLocInfo, Value *MaptypesArg, Value *MapnamesArg, struct MapperAllocas &MapperAllocas, int64_t DeviceID, unsigned NumOperands)
Create the call for the target mapper function.
LLVM_ABI InsertPointOrErrorTy createDistribute(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB)
Generator for #omp distribute
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 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, DebugLoc OutlinedFnLoc={})
Generator for 'omp target'.
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.
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, bool FreeAgent=false)
Generator for #omp taskloop
std::forward_list< ScanInfo > ScanInfos
Collection of owned ScanInfo objects that eventually need to be free'd.
static LLVM_ABI void writeTeamsForKernel(const Triple &T, Function &Kernel, int32_t LB, int32_t UB)
LLVM_ABI Value * calculateCanonicalLoopTripCount(const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, const Twine &Name="loop")
Calculate the trip count of a canonical loop.
LLVM_ABI InsertPointOrErrorTy createBarrier(const LocationDescription &Loc, omp::Directive Kind, bool ForceSimpleCall=false, bool CheckCancelFlag=true)
Emitter methods for OpenMP directives.
LLVM_ABI void setCorrectMemberOfFlag(omp::OpenMPOffloadMappingFlags &Flags, omp::OpenMPOffloadMappingFlags MemberOfFlag)
Given an initial flag set, this function modifies it to contain the passed in MemberOfFlag generated ...
LLVM_ABI Error emitOffloadingArraysAndArgs(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info, TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo, CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous=false, bool ForEndCall=false, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr)
Allocates memory for and populates the arrays required for offloading (offload_{baseptrs|ptrs|mappers...
LLVM_ABI Constant * getOrCreateDefaultSrcLocStr(uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the default source location.
LLVM_ABI InsertPointOrErrorTy createCritical(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst)
Generator for 'omp critical'.
LLVM_ABI void createError(const LocationDescription &Loc, bool IsFatal, Value *Message)
Generate a call to the runtime to emit the diagnostic of an OpenMP error directive with at(execution)...
LLVM_ABI void createOffloadEntry(Constant *ID, Constant *Addr, uint64_t Size, int32_t Flags, GlobalValue::LinkageTypes, StringRef Name="")
Creates offloading entry for the provided entry ID ID, address Addr, size Size, and flags Flags.
static LLVM_ABI unsigned getOpenMPDefaultSimdAlign(const Triple &TargetTriple, const StringMap< bool > &Features)
Get the default alignment value for given target.
LLVM_ABI unsigned getFlagMemberOffset()
Get the offset of the OMP_MAP_MEMBER_OF field.
LLVM_ABI InsertPointOrErrorTy applyWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP, bool NeedsBarrier, llvm::omp::ScheduleKind SchedKind=llvm::omp::OMP_SCHEDULE_Default, Value *ChunkSize=nullptr, bool HasSimdModifier=false, bool HasMonotonicModifier=false, bool HasNonmonotonicModifier=false, bool HasOrderedClause=false, omp::WorksharingLoopType LoopType=omp::WorksharingLoopType::ForStaticLoop, bool NoLoop=false, bool HasDistSchedule=false, Value *DistScheduleChunkSize=nullptr)
Modifies the canonical loop to be a workshare loop.
LLVM_ABI InsertPointOrErrorTy createAtomicCapture(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, AtomicOpValue &V, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: — Only Scalar data types V = X; X = X BinOp Expr ,...
LLVM_ABI CanonicalLoopInfo * createLoopSkeleton(DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore, BasicBlock *PostInsertBefore, const Twine &Name={}, bool IsCollapsed=false)
Create the control flow structure of a canonical OpenMP loop.
LLVM_ABI void createOffloadEntriesAndInfoMetadata(EmitMetadataErrorReportFunctionTy &ErrorReportFunction)
LLVM_ABI void applySimd(CanonicalLoopInfo *Loop, MapVector< Value *, Value * > AlignedVars, Value *IfCond, omp::OrderKind Order, ConstantInt *Simdlen, ConstantInt *Safelen)
Add metadata to simd-ize a loop.
SmallVector< std::unique_ptr< OutlineInfo >, 16 > OutlineInfos
Collection of regions that need to be outlined during finalization.
LLVM_ABI InsertPointOrErrorTy createAtomicUpdate(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: X = X BinOp Expr ,or X = Expr BinOp X For complex Operations: X = ...
std::function< std::tuple< std::string, uint64_t >()> FileIdentifierInfoCallbackTy
bool isLastFinalizationInfoCancellable(omp::Directive DK)
Return true if the last entry in the finalization stack is of kind DK and cancellable.
LLVM_ABI InsertPointTy emitTargetKernel(const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return, Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads, Value *HostPtr, ArrayRef< Value * > KernelArgs)
Generate a target region entry call.
LLVM_ABI GlobalVariable * createOffloadMaptypes(SmallVectorImpl< uint64_t > &Mappings, std::string VarName)
Create the global variable holding the offload mappings information.
LLVM_ABI Expected< Function * > emitUserDefinedMapper(function_ref< MapInfosOrErrorTy(InsertPointTy CodeGenIP, llvm::Value *PtrPHI, llvm::Value *BeginArg)> PrivAndGenMapInfoCB, llvm::Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB, bool PreserveMemberOfFlags=false, bool PropagatePresentToPointee=false)
Emit the user-defined mapper function.
LLVM_ABI CallInst * createCachedThreadPrivate(const LocationDescription &Loc, llvm::Value *Pointer, llvm::ConstantInt *Size, const llvm::Twine &Name=Twine(""))
Create a runtime call for kmpc_threadprivate_cached.
IRBuilder Builder
The LLVM-IR Builder used to create IR.
LLVM_ABI GlobalValue * createGlobalFlag(unsigned Value, StringRef Name)
Create a hidden global flag Name in the module with initial value Value.
LLVM_ABI void emitOffloadingArraysArgument(IRBuilderBase &Builder, OpenMPIRBuilder::TargetDataRTArgs &RTArgs, OpenMPIRBuilder::TargetDataInfo &Info, bool ForEndCall=false)
Emit the arguments to be passed to the runtime library based on the arrays of base pointers,...
LLVM_ABI InsertPointOrErrorTy createMasked(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, Value *Filter)
Generator for 'omp masked'.
LLVM_ABI Expected< CanonicalLoopInfo * > createCanonicalLoop(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *TripCount, const Twine &Name="loop")
Generator for the control flow structure of an OpenMP canonical loop.
function_ref< Expected< InsertPointTy >( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value *DestPtr, Value *SrcPtr)> TaskDupCallbackTy
Callback type for task duplication function code generation.
LLVM_ABI Value * getSizeInBytes(Value *BasePtr)
Computes the size of type in bytes.
llvm::function_ref< llvm::Error( InsertPointTy BodyIP, llvm::Value *LinearIV)> IteratorBodyGenTy
LLVM_ABI FunctionCallee createDispatchDeinitFunction()
Returns __kmpc_dispatch_deinit runtime function.
LLVM_ABI void registerTargetGlobalVariable(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy, Constant *Addr)
Registers a target variable for device or host.
LLVM_ABI void createTargetDeinit(const LocationDescription &Loc, int32_t TeamsReductionDataSize=0)
Create a runtime call for kmpc_target_deinit.
BodyGenTy
Type of BodyGen to use for region codegen.
LLVM_ABI CanonicalLoopInfo * fuseLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops)
Fuse a sequence of loops.
LLVM_ABI void emitX86DeclareSimdFunction(llvm::Function *Fn, unsigned NumElements, const llvm::APSInt &VLENVal, llvm::ArrayRef< DeclareSimdAttrTy > ParamAttrs, DeclareSimdBranch Branch)
Emit x86 vector-function ABI attributes for a declare simd function.
SmallVector< llvm::Function *, 16 > ConstantAllocaRaiseCandidates
A collection of candidate target functions that's constant allocas will attempt to be raised on a cal...
OffloadEntriesInfoManager OffloadInfoManager
Info manager to keep track of target regions.
static LLVM_ABI std::pair< int32_t, int32_t > readTeamBoundsForKernel(const Triple &T, Function &Kernel)
Read/write a bounds on teams for Kernel.
const std::string ompOffloadInfoName
OMP Offload Info Metadata name string.
Expected< InsertPointTy > InsertPointOrErrorTy
Type used to represent an insertion point or an error value.
LLVM_ABI InsertPointTy createCopyPrivate(const LocationDescription &Loc, llvm::Value *BufSize, llvm::Value *CpyBuf, llvm::Value *CpyFn, llvm::Value *DidIt)
Generator for __kmpc_copyprivate.
LLVM_ABI InsertPointOrErrorTy createSections(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< StorableBodyGenCallbackTy > SectionCBs, PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait)
Generator for 'omp sections'.
std::function< void(EmitMetadataErrorKind, TargetRegionEntryInfo)> EmitMetadataErrorReportFunctionTy
Callback function type.
function_ref< InsertPointOrErrorTy( Argument &Arg, Value *Input, Value *&RetVal, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< InsertPointTy > DeallocIPs)> TargetGenArgAccessorsCallbackTy
LLVM_ABI Expected< ScanInfo * > scanInfoInitialize()
Creates a ScanInfo object, allocates and returns the pointer.
LLVM_ABI InsertPointOrErrorTy emitTargetTask(TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc, OpenMPIRBuilder::InsertPointTy AllocaIP, const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs, bool HasNoWait)
Generate a target-task for the target construct.
LLVM_ABI InsertPointTy createAtomicRead(const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V, AtomicOrdering AO, InsertPointTy AllocaIP)
Emit atomic Read for : V = X — Only Scalar data types.
function_ref< Error(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> BodyGenCallbackTy
Callback type for body (=inner region) code generation.
bool updateToLocation(const LocationDescription &Loc)
Update the internal location to Loc.
LLVM_ABI void createFlush(const LocationDescription &Loc)
Generator for 'omp flush'.
LLVM_ABI void createTaskwait(const LocationDescription &Loc, DependenciesInfo Dependencies={})
Generator for 'omp taskwait'.
LLVM_ABI Constant * getAddrOfDeclareTargetVar(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, Type *LlvmPtrTy, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage)
Retrieve (or create if non-existent) the address of a declare target variable, used in conjunction wi...
origPtr *with the address space normalization required by the runtime entry point *The NULL descriptor makes the runtime walk the enclosing taskgroups to *find the matching task_reduction registration for the item The lookups *are emitted at p Loc
EmitMetadataErrorKind
The kind of errors that can occur when emitting the offload entries and metadata.
unsigned getOpcode() const
Return the opcode for this Instruction or ConstantExpr.
Definition Operator.h:43
The optimization diagnostic interface.
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
Pseudo-analysis pass that exposes the PassInstrumentation to pass managers.
Class to represent pointers.
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
static LLVM_ABI PointerType * get(LLVMContext &C, unsigned AddressSpace)
This constructs an opaque pointer to an object in a numbered address space.
Definition Type.cpp:887
PostDominatorTree Class - Concrete subclass of DominatorTree that is used to compute the post-dominat...
Analysis pass that exposes the ScalarEvolution for a function.
LLVM_ABI ScalarEvolution run(Function &F, FunctionAnalysisManager &AM)
The main scalar evolution driver.
ScanInfo holds the information to assist in lowering of Scan reduction.
llvm::SmallDenseMap< llvm::Value *, llvm::Value * > * ScanBuffPtrs
Maps the private reduction variable to the pointer of the temporary buffer.
llvm::BasicBlock * OMPScanLoopExit
Exit block of loop body.
llvm::Value * IV
Keeps track of value of iteration variable for input/scan loop to be used for Scan directive lowering...
llvm::BasicBlock * OMPAfterScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanInit
Block before loop body where scan initializations are done.
llvm::BasicBlock * OMPBeforeScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanFinish
Block after loop body where scan finalizations are done.
llvm::Value * Span
Stores the span of canonical loop being lowered to be used for temporary buffer allocation or Finaliz...
bool OMPFirstScanLoop
If true, it indicates Input phase is lowered; else it indicates ScanPhase is lowered.
llvm::BasicBlock * OMPScanDispatch
Controls the flow to before or after scan blocks.
A vector that has set insertion semantics.
Definition SetVector.h:57
bool remove_if(UnaryPredicate P)
Remove items from the set vector based on a predicate function.
Definition SetVector.h:236
bool empty() const
Determine if the SetVector is empty or not.
Definition SetVector.h:100
This is a 'bitvector' (really, a variable-sized bit array), optimized for the case when the array is ...
SmallBitVector & set()
bool test(unsigned Idx) const
Returns true if bit Idx is set.
bool all() const
Returns true if all bits are set.
bool any() const
Returns true if any bit is set.
A templated base class for SmallPtrSet which provides the typesafe interface that is common across al...
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition SmallSet.h:134
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
void append(StringRef RHS)
Append from a StringRef.
Definition SmallString.h:68
StringRef str() const
Explicit conversion to StringRef.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
void reserve(size_type N)
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void resize(size_type N)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
void setAlignment(Align Align)
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this store instruction.
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:129
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:250
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:467
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:662
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:1137
bool isX86() const
Tests whether the target is x86 (32- or 64-bit).
Definition Triple.h:1197
bool isWasm() const
Tests whether the target is wasm (32- and 64-bit).
Definition Triple.h:1211
bool isSystemZ() const
Tests whether the target is SystemZ.
Definition Triple.h:1194
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:300
LLVM_ABI unsigned getIntegerBitWidth() const
LLVM_ABI Type * getStructElementType(unsigned N) const
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:299
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:277
LLVM_ABI unsigned getPointerAddressSpace() const
Get the address space of this pointer or pointer vector type.
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:272
bool isStructTy() const
True if this is an instance of StructType.
Definition Type.h:271
LLVM_ABI unsigned getScalarSizeInBits() const LLVM_READONLY
If this is a vector type, return the getPrimitiveSizeInBits value for the element type.
Definition Type.cpp:222
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:252
static LLVM_ABI IntegerType * getIntNTy(LLVMContext &C, unsigned N)
Definition Type.cpp:303
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:257
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:441
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
iterator_range< user_iterator > users()
Definition Value.h:428
User * user_back()
Definition Value.h:414
LLVM_ABI Align getPointerAlignment(const DataLayout &DL) const
Returns an alignment of the pointer value.
Definition Value.cpp:1002
LLVM_ABI bool hasNUses(unsigned N) const
Return true if this Value has exactly N uses.
Definition Value.cpp:147
LLVM_ABI User * getUniqueUndroppableUser()
Return true if there is exactly one unique user of this value that cannot be dropped (that user can h...
Definition Value.cpp:185
LLVM_ABI const Value * stripPointerCasts() const
Strip off pointer casts, all-zero GEPs and address space casts.
Definition Value.cpp:713
bool use_empty() const
Definition Value.h:348
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:382
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.
The virtual file system interface.
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(const Twine &Name, int64_t FileSize=-1, bool RequiresNullTerminator=true, bool IsVolatile=false, bool IsText=true)
This is a convenience method that opens a file, gets its content and then closes the file.
virtual llvm::ErrorOr< Status > status(const Twine &Path)=0
Get the status of the entry at Path, if one exists.
CallInst * Call
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
@ AMDGPU_KERNEL
Used for AMDGPU code object kernels.
@ SPIR_KERNEL
Used for SPIR kernel functions.
@ PTX_Kernel
Call to a PTX kernel. Passes all arguments in parameter space.
@ BasicBlock
Various leaf nodes.
Definition ISDOpcodes.h:81
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
Flag
These should be considered private to the implementation of the MCInstrDesc class.
constexpr StringLiteral MaxNTID("nvvm.maxntid")
constexpr StringLiteral MaxClusterRank("nvvm.maxclusterrank")
initializer< Ty > init(const Ty &Val)
@ User
could "use" a pointer
LLVM_ABI GlobalVariable * emitOffloadingEntry(Module &M, object::OffloadKind Kind, Constant *Addr, StringRef Name, uint64_t Size, uint32_t Flags, uint64_t Data, Constant *AuxAddr=nullptr)
Definition Utility.cpp:104
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.
EnumSet< Property, Property_enumSize > Properties
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:577
detail::zippy< detail::zip_shortest, T, U, Args... > zip(T &&t, U &&u, Args &&...args)
zip iterator for two or more iteratable types.
Definition STLExtras.h:830
LLVM_ABI unsigned computeUnrollCount(Loop *L, const TargetTransformInfo &TTI, DominatorTree &DT, LoopInfo *LI, AssumptionCache *AC, ScalarEvolution &SE, const SmallPtrSetImpl< const Value * > &EphValues, OptimizationRemarkEmitter *ORE, unsigned TripCount, unsigned MaxTripCount, bool MaxOrZero, unsigned TripMultiple, const UnrollCostEstimator &UCE, TargetTransformInfo::UnrollingPreferences &UP, TargetTransformInfo::PeelingPreferences &PP)
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1739
hash_code hash_value(const FixedPointSemantics &Val)
LLVM_ABI Expected< std::unique_ptr< Module > > parseBitcodeFile(MemoryBufferRef Buffer, LLVMContext &Context, ParserCallbacks Callbacks={})
Read the specified bitcode file, returning the module.
detail::zippy< detail::zip_first, T, U, Args... > zip_equal(T &&t, U &&u, Args &&...args)
zip iterator that assumes that all iteratees have the same length.
Definition STLExtras.h:840
LLVM_ABI BasicBlock * CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap, const Twine &NameSuffix="", Function *F=nullptr, ClonedCodeInfo *CodeInfo=nullptr, bool MapAtoms=true)
Return a copy of the specified basic block, but without embedding the block into a particular functio...
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition STLExtras.h:2554
unsigned getPointerAddressSpace(const Type *T)
Definition SPIRVUtils.h:395
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto successors(const MachineBasicBlock *BB)
@ Load
The value being inserted comes from a load (InsertElement only).
@ Store
The extracted value is stored (ExtractElement only).
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
testing::Matcher< const detail::ErrorHolder & > Failed()
Definition Error.h:198
constexpr from_range_t from_range
auto dyn_cast_if_present(const Y &Val)
dyn_cast_if_present<X> - Functionally identical to dyn_cast, except that a null (or none in the case ...
Definition Casting.h:732
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE()
LLVM_ABI BasicBlock * splitBB(IRBuilderBase::InsertPoint IP, bool CreateBranch, DebugLoc DL, llvm::Twine Name={})
Split a BasicBlock at an InsertPoint, even if the block is degenerate (missing the terminator).
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
Definition STLExtras.h:2208
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:633
LLVM_ABI TargetTransformInfo::UnrollingPreferences gatherUnrollingPreferences(Loop *L, ScalarEvolution &SE, const TargetTransformInfo &TTI, BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, llvm::OptimizationRemarkEmitter &ORE, int OptLevel, std::optional< unsigned > UserThreshold, std::optional< bool > UserAllowPartial, std::optional< bool > UserRuntime, std::optional< bool > UserUpperBound, std::optional< unsigned > UserFullUnrollMaxCount)
Gather the various unrolling parameters based on the defaults, compiler flags, TTI overrides and user...
std::string utostr(uint64_t X, bool isNeg=false)
void * PointerTy
ErrorOr< T > expectedToErrorOrAndEmitErrors(LLVMContext &Ctx, Expected< T > Val)
bool isa_and_nonnull(const Y &Val)
Definition Casting.h:676
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
LLVM_ABI bool convertUsersOfConstantsToInstructions(ArrayRef< Constant * > Consts, Function *RestrictToFunc=nullptr, bool RemoveDeadConstants=true, bool IncludeSelf=false)
Replace constant expressions users of the given constants with instructions.
unsigned Log2_32(uint32_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
Definition MathExtras.h:326
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:177
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition Format.h:102
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
AtomicOrdering
Atomic ordering for LLVM's memory model.
constexpr T divideCeil(U Numerator, V Denominator)
Returns the integer ceil(Numerator / Denominator).
Definition MathExtras.h:389
TargetTransformInfo TTI
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
LLVM_ABI bool MergeBlockIntoPredecessor(BasicBlock *BB, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, MemoryDependenceResults *MemDep=nullptr, bool PredecessorWithTwoSuccessors=false, DominatorTree *DT=nullptr)
Attempts to merge a block into its predecessor, if possible.
@ Mul
Product of integers.
@ Add
Sum of integers.
LLVM_ABI BasicBlock * SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt, DominatorTree *DT, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, const Twine &BBName="")
Split the specified block at the specified instruction.
IntPtrTy
Definition InstrProf.h:82
DWARFExpression::Operation Op
LLVM_ABI void remapInstructionsInBlocks(ArrayRef< BasicBlock * > Blocks, ValueToValueMapTy &VMap)
Remaps instructions in Blocks using the mapping in VMap.
ArrayRef(const T &OneElt) -> ArrayRef< T >
OutputIt copy(R &&Range, OutputIt Out)
Definition STLExtras.h:1885
constexpr unsigned BitWidth
ValueMap< const Value *, WeakTrackingVH > ValueToValueMapTy
LLVM_ABI void spliceBB(IRBuilderBase::InsertPoint IP, BasicBlock *New, bool CreateBranch, DebugLoc DL)
Move the instruction after an InsertPoint to the beginning of another BasicBlock.
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
constexpr auto seq(T Begin, T End)
Iterate over an integral type from Begin up to - but not including - End.
Definition Sequence.h:341
auto predecessors(const MachineBasicBlock *BB)
auto filter_to_vector(ContainerTy &&C, PredicateFn &&Pred)
Filter a range to a SmallVector with the element types deduced.
PointerUnion< const Value *, const PseudoSourceValue * > ValueType
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Next
Definition InstrProf.h:147
LLVM_ABI Constant * ConstantFoldInsertValueInstruction(Constant *Agg, Constant *Val, ArrayRef< unsigned > Idxs)
Attempt to constant fold an insertvalue instruction with the specified operands and indices.
@ Continue
Definition DWP.h:26
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
LLVM_ABI void DeleteDeadBlocks(ArrayRef< BasicBlock * > BBs, DomTreeUpdater *DTU=nullptr, bool KeepOneInputPHIs=false)
Delete the specified blocks from BB.
bool to_integer(StringRef S, N &Num, unsigned Base=0)
Convert the string S to an integer of the specified type using the radix Base. If Base is 0,...
static auto filterDbgVars(iterator_range< simple_ilist< DbgRecord >::iterator > R)
Filter the DbgRecord range to DbgVariableRecord types only and downcast.
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
static LLVM_ABI void collectEphemeralValues(const Loop *L, AssumptionCache *AC, SmallPtrSetImpl< const Value * > &EphValues)
Collect a loop's ephemeral values (those used only by an assume or similar intrinsics in the loop).
This struct is a compact representation of a valid (power of two) or undefined (0) alignment.
Definition Alignment.h:106
A struct to pack the relevant information for an OpenMP affinity clause.
a struct to pack relevant information while generating atomic Ops
A struct to pack the relevant information for an OpenMP depend clause.
omp::RTLDependenceKindTy DepKind
A struct to pack static and dynamic dependency information for a task.
LLVM_ABI Error mergeFiniBB(IRBuilderBase &Builder, BasicBlock *ExistingFiniBB)
For cases where there is an unavoidable existing finalization block (e.g.
LLVM_ABI Expected< BasicBlock * > getFiniBB(IRBuilderBase &Builder)
The basic block to which control should be transferred to implement the FiniCB.
Description of a LLVM-IR insertion point (IP) and a debug/source location (filename,...
This structure contains combined information generated for mappable clauses, including base pointers,...
MapDeviceInfoArrayTy DevicePointers
StructNonContiguousInfo NonContigInfo
Helper that contains information about regions we need to outline during finalization.
void collectBlocks(SmallPtrSetImpl< BasicBlock * > &BlockSet, SmallVectorImpl< BasicBlock * > &BlockVector)
Collect all blocks in between EntryBB and ExitBB in both the given vector and set.
virtual std::unique_ptr< CodeExtractor > createCodeExtractor(ArrayRef< BasicBlock * > Blocks, bool ArgsInZeroAddressSpace, Twine Suffix=Twine(""))
Create a CodeExtractor instance based on the information stored in this structure,...
Information about an OpenMP reduction.
EvalKind EvaluationKind
Reduction evaluation kind - scalar, complex or aggregate.
ReductionGenAtomicCBTy AtomicReductionGen
Callback for generating the atomic reduction body, may be null.
ReductionGenCBTy ReductionGen
Callback for generating the reduction body.
Value * Variable
Reduction variable of pointer type.
Value * PrivateVariable
Thread-private partial reduction variable.
ReductionGenClangCBTy ReductionGenClang
Clang callback for generating the reduction body.
Type * ElementType
Reduction element type, must match pointee type of variable.
ReductionGenDataPtrPtrCBTy DataPtrPtrGen
Container for the arguments used to pass data to the runtime library.
Value * SizesArray
The array of sizes passed to the runtime library.
Value * PointersArray
The array of section pointers passed to the runtime library.
Value * MappersArray
The array of user-defined mappers passed to the runtime library.
Value * MapTypesArrayEnd
The array of map types passed to the runtime library for the end of the region, or nullptr if there a...
Value * BasePointersArray
The array of base pointer passed to the runtime library.
Value * MapTypesArray
The array of map types passed to the runtime library for the beginning of the region or for the entir...
Value * MapNamesArray
The array of original declaration names of mapped pointers sent to the runtime library for debugging.
Data structure that contains the needed information to construct the kernel args vector.
bool StrictBlocks
True if the kernel strictly requires the number of blocks and threads above to run.
ArrayRef< Value * > NumThreads
The number of threads.
TargetDataRTArgs RTArgs
Arguments passed to the runtime library.
Value * NumIterations
The number of iterations.
Value * DynCGroupMem
The size of the dynamic shared memory.
unsigned NumTargetItems
Number of arguments passed to the runtime library.
bool HasNoWait
True if the kernel has 'no wait' clause.
ArrayRef< Value * > NumTeams
The number of teams.
omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback
The fallback mechanism for the shared memory.
Container to pass the default attributes with which a kernel must be launched, used to set kernel att...
Container to pass LLVM IR runtime values or constants related to the number of teams and threads with...
Value * DeviceID
Device ID value used in the kernel launch.
Value * LoopTripCount
Total number of iterations of the SPMD or Generic-SPMD kernel or null if it is a generic kernel.
SmallVector< Value * > MaxThreads
'parallel' construct 'num_threads' clause value, if present and it is an SPMD kernel.
Data structure to contain the information needed to uniquely identify a target entry.
static LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, StringRef ParentName, unsigned DeviceID, unsigned FileID, unsigned Line, unsigned Count)
static constexpr const char * KernelNamePrefix
The prefix used for kernel names.
static LLVM_ABI const Target * lookupTarget(const Triple &TheTriple, std::string &Error)
lookupTarget - Lookup a target based on a target triple.
Parameters that control the generic loop unrolling transformation.
unsigned Threshold
The cost threshold for the unrolled loop.
bool Force
Apply loop unroll on any kind of loop (mainly to loops that fail runtime unrolling).
unsigned PartialOptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size, like OptSizeThreshold,...
unsigned PartialThreshold
The cost threshold for the unrolled loop, like Threshold, but used for partial/runtime unrolling (set...
unsigned OptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size (set to UINT_MAX to disable).
Defines various target-specific GPU grid values that must be consistent between host RTL (plugin),...