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, Type *VarType,
549 const Twine &Name = Twine(""),
550 AddrSpaceCastInst **CastedAlloc = nullptr) override {
551 return OMPBuilder.createOMPAllocShared(AllocaIP, VarType, Name);
552 }
553
554 virtual Instruction *deallocateVar(IRBuilder<>::InsertPoint DeallocIP,
555 Value *Var, Type *VarType) override {
556 return OMPBuilder.createOMPFreeShared(DeallocIP, Var, VarType);
557 }
558};
559
560/// Helper storing information about regions to outline using device shared
561/// memory for intermediate allocations.
562struct DeviceSharedMemOutlineInfo : public OpenMPIRBuilder::OutlineInfo {
563 OpenMPIRBuilder &OMPBuilder;
564
565 DeviceSharedMemOutlineInfo(OpenMPIRBuilder &OMPBuilder)
566 : OMPBuilder(OMPBuilder) {}
567 virtual ~DeviceSharedMemOutlineInfo() = default;
568
569 virtual std::unique_ptr<CodeExtractor>
570 createCodeExtractor(ArrayRef<BasicBlock *> Blocks,
571 bool ArgsInZeroAddressSpace,
572 Twine Suffix = Twine("")) override;
573};
574
575} // anonymous namespace
576
578 : RequiresFlags(OMP_REQ_UNDEFINED) {}
579
582 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,
583 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)
586 RequiresFlags(OMP_REQ_UNDEFINED) {
587 if (HasRequiresReverseOffload)
588 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
589 if (HasRequiresUnifiedAddress)
590 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
591 if (HasRequiresUnifiedSharedMemory)
592 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
593 if (HasRequiresDynamicAllocators)
594 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
595}
596
598 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;
599}
600
602 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;
603}
604
606 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;
607}
608
610 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;
611}
612
614 return hasRequiresFlags() ? RequiresFlags
615 : static_cast<int64_t>(OMP_REQ_NONE);
616}
617
619 if (Value)
620 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
621 else
622 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;
623}
624
626 if (Value)
627 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
628 else
629 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;
630}
631
633 if (Value)
634 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
635 else
636 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;
637}
638
640 if (Value)
641 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
642 else
643 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;
644}
645
646//===----------------------------------------------------------------------===//
647// OpenMPIRBuilder
648//===----------------------------------------------------------------------===//
649
652 SmallVector<Value *> &ArgsVector) {
654 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);
655 auto Int32Ty = Type::getInt32Ty(Builder.getContext());
656 constexpr size_t MaxDim = 3;
657 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));
658
659 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);
660
661 Value *DynCGroupMemFallbackFlag =
662 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));
663 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);
664
665 Value *StrictBlocksFlag = Builder.getInt64(KernelArgs.StrictBlocks);
666 Value *StrictThreadsFlag = Builder.getInt64(KernelArgs.StrictThreads);
667
668 StrictBlocksFlag = Builder.CreateShl(StrictBlocksFlag, 6);
669 StrictThreadsFlag = Builder.CreateShl(StrictThreadsFlag, 7);
670
671 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
672 Flags = Builder.CreateOr(Flags, StrictBlocksFlag);
673 Flags = Builder.CreateOr(Flags, StrictThreadsFlag);
674
675 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
676
677 Value *NumTeams3D =
678 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
679 Value *NumThreads3D =
680 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
681 for (unsigned I :
682 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
683 NumTeams3D =
684 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
685 for (unsigned I :
686 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
687 NumThreads3D =
688 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
689
690 ArgsVector = {Version,
691 PointerNum,
692 KernelArgs.RTArgs.BasePointersArray,
693 KernelArgs.RTArgs.PointersArray,
694 KernelArgs.RTArgs.SizesArray,
695 KernelArgs.RTArgs.MapTypesArray,
696 KernelArgs.RTArgs.MapNamesArray,
697 KernelArgs.RTArgs.MappersArray,
698 KernelArgs.NumIterations,
699 Flags,
700 NumTeams3D,
701 NumThreads3D,
702 KernelArgs.DynCGroupMem};
703}
704
706 LLVMContext &Ctx = Fn.getContext();
707
708 // Get the function's current attributes.
709 auto Attrs = Fn.getAttributes();
710 auto FnAttrs = Attrs.getFnAttrs();
711 auto RetAttrs = Attrs.getRetAttrs();
713 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
714 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
715
716 // Add AS to FnAS while taking special care with integer extensions.
717 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
718 bool Param = true) -> void {
719 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
720 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
721 if (HasSignExt || HasZeroExt) {
722 assert(AS.getNumAttributes() == 1 &&
723 "Currently not handling extension attr combined with others.");
724 if (Param) {
725 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
726 FnAS = FnAS.addAttribute(Ctx, AK);
727 } else if (auto AK =
728 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
729 FnAS = FnAS.addAttribute(Ctx, AK);
730 } else {
731 FnAS = FnAS.addAttributes(Ctx, AS);
732 }
733 };
734
735#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
736#include "llvm/Frontend/OpenMP/OMPKinds.def"
737
738 // Add attributes to the function declaration.
739 switch (FnID) {
740#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
741 case Enum: \
742 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
743 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
744 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
745 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
746 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
747 break;
748#include "llvm/Frontend/OpenMP/OMPKinds.def"
749 default:
750 // Attributes are optional.
751 break;
752 }
753}
754
757 FunctionType *FnTy = nullptr;
758 Function *Fn = nullptr;
759
760 // Try to find the declation in the module first.
761 switch (FnID) {
762#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
763 case Enum: \
764 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
765 IsVarArg); \
766 Fn = M.getFunction(Str); \
767 break;
768#include "llvm/Frontend/OpenMP/OMPKinds.def"
769 }
770
771 if (!Fn) {
772 // Create a new declaration if we need one.
773 switch (FnID) {
774#define OMP_RTL(Enum, Str, ...) \
775 case Enum: \
776 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
777 break;
778#include "llvm/Frontend/OpenMP/OMPKinds.def"
779 }
780 Fn->setCallingConv(Config.getRuntimeCC());
781 // Add information if the runtime function takes a callback function
782 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
783 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
784 LLVMContext &Ctx = Fn->getContext();
785 MDBuilder MDB(Ctx);
786 // Annotate the callback behavior of the runtime function:
787 // - The callback callee is argument number 2 (microtask).
788 // - The first two arguments of the callback callee are unknown (-1).
789 // - All variadic arguments to the runtime function are passed to the
790 // callback callee.
791 Fn->addMetadata(
792 LLVMContext::MD_callback,
794 2, {-1, -1}, /* VarArgsArePassed */ true)}));
795 }
796 }
797
798 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
799 << " with type " << *Fn->getFunctionType() << "\n");
800 addAttributes(FnID, *Fn);
801
802 } else {
803 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
804 << " with type " << *Fn->getFunctionType() << "\n");
805 }
806
807 assert(Fn && "Failed to create OpenMP runtime function");
808
809 return {FnTy, Fn};
810}
811
814 if (!FiniBB) {
815 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
817 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
818 Builder.SetInsertPoint(FiniBB);
819 // FiniCB adds the branch to the exit stub.
820 if (Error Err = FiniCB(Builder.saveIP()))
821 return Err;
822 }
823 return FiniBB;
824}
825
827 BasicBlock *OtherFiniBB) {
828 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
829 if (!FiniBB) {
830 FiniBB = OtherFiniBB;
831
832 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
833 if (Error Err = FiniCB(Builder.saveIP()))
834 return Err;
835
836 return Error::success();
837 }
838
839 // Move instructions from FiniBB to the start of OtherFiniBB.
840 auto EndIt = FiniBB->end();
841 if (FiniBB->size() >= 1)
842 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
843 EndIt = Prev;
844 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
845 EndIt);
846
847 FiniBB->replaceAllUsesWith(OtherFiniBB);
848 FiniBB->eraseFromParent();
849 FiniBB = OtherFiniBB;
850 return Error::success();
851}
852
855 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
856 assert(Fn && "Failed to create OpenMP runtime function pointer");
857 return Fn;
858}
859
862 StringRef Name) {
863 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
864 Call->setCallingConv(Config.getRuntimeCC());
865 return Call;
866}
867
868void OpenMPIRBuilder::initialize() { initializeTypes(M); }
869
872 BasicBlock &EntryBlock = Function->getEntryBlock();
873 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
874
875 // Loop over blocks looking for constant allocas, skipping the entry block
876 // as any allocas there are already in the desired location.
877 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
878 Block++) {
879 for (auto Inst = Block->getReverseIterator()->begin();
880 Inst != Block->getReverseIterator()->end();) {
882 Inst++;
884 continue;
885 AllocaInst->moveBeforePreserving(MoveLocInst);
886 } else {
887 Inst++;
888 }
889 }
890 }
891}
892
895
896 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
897 // TODO: For now, we support simple static allocations, we might need to
898 // move non-static ones as well. However, this will need further analysis to
899 // move the lenght arguments as well.
901 };
902
903 for (llvm::Instruction &Inst : Block)
905 if (ShouldHoistAlloca(*AllocaInst))
906 AllocasToMove.push_back(AllocaInst);
907
908 auto InsertPoint =
909 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
910
911 for (llvm::Instruction *AllocaInst : AllocasToMove)
913}
914
916 PostDominatorTree PostDomTree(*Func);
917 for (llvm::BasicBlock &BB : *Func)
918 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
920}
921
923 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
925 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
926 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
927 // Skip functions that have not finalized yet; may happen with nested
928 // function generation.
929 if (Fn && OI->getFunction() != Fn) {
930 DeferredOutlines.push_back(std::move(OI));
931 continue;
932 }
933
934 ParallelRegionBlockSet.clear();
935 Blocks.clear();
936 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
937
938 Function *OuterFn = OI->getFunction();
939 CodeExtractorAnalysisCache CEAC(*OuterFn);
940 // If we generate code for the target device, we need to allocate
941 // struct for aggregate params in the device default alloca address space.
942 // OpenMP runtime requires that the params of the extracted functions are
943 // passed as zero address space pointers. This flag ensures that
944 // CodeExtractor generates correct code for extracted functions
945 // which are used by OpenMP runtime.
946 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
947 std::unique_ptr<CodeExtractor> Extractor =
948 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
949
950 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
951 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
952 << " Exit: " << OI->ExitBB->getName() << "\n");
953 assert(Extractor->isEligible() &&
954 "Expected OpenMP outlining to be possible!");
955
956 for (auto *V : OI->ExcludeArgsFromAggregate)
957 Extractor->excludeArgFromAggregate(V);
958
959 Function *OutlinedFn =
960 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
961
962 // Forward target-cpu, target-features attributes to the outlined function.
963 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
964 if (TargetCpuAttr.isStringAttribute())
965 OutlinedFn->addFnAttr(TargetCpuAttr);
966
967 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
968 if (TargetFeaturesAttr.isStringAttribute())
969 OutlinedFn->addFnAttr(TargetFeaturesAttr);
970
971 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
972 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
973 assert(OutlinedFn->getReturnType()->isVoidTy() &&
974 "OpenMP outlined functions should not return a value!");
975
976 // For compability with the clang CG we move the outlined function after the
977 // one with the parallel region.
978 OutlinedFn->removeFromParent();
979 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
980
981 // Remove the artificial entry introduced by the extractor right away, we
982 // made our own entry block after all.
983 {
984 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
985 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
986 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
987 // Move instructions from the to-be-deleted ArtificialEntry to the entry
988 // basic block of the parallel region. CodeExtractor generates
989 // instructions to unwrap the aggregate argument and may sink
990 // allocas/bitcasts for values that are solely used in the outlined region
991 // and do not escape.
992 assert(!ArtificialEntry.empty() &&
993 "Expected instructions to add in the outlined region entry");
994 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
995 End = ArtificialEntry.rend();
996 It != End;) {
997 Instruction &I = *It;
998 It++;
999
1000 if (I.isTerminator()) {
1001 // Absorb any debug value that terminator may have
1002 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
1003 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
1004 continue;
1005 }
1006
1007 I.moveBeforePreserving(*OI->EntryBB,
1008 OI->EntryBB->getFirstInsertionPt());
1009 }
1010
1011 OI->EntryBB->moveBefore(&ArtificialEntry);
1012 ArtificialEntry.eraseFromParent();
1013 }
1014 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
1015 assert(OutlinedFn && OutlinedFn->hasNUses(1));
1016
1017 // Run a user callback, e.g. to add attributes.
1018 if (OI->PostOutlineCB)
1019 OI->PostOutlineCB(*OutlinedFn);
1020
1021 if (OI->FixUpNonEntryAllocas)
1023 }
1024
1025 // Remove work items that have been completed.
1026 OutlineInfos = std::move(DeferredOutlines);
1027
1028 // The createTarget functions embeds user written code into
1029 // the target region which may inject allocas which need to
1030 // be moved to the entry block of our target or risk malformed
1031 // optimisations by later passes, this is only relevant for
1032 // the device pass which appears to be a little more delicate
1033 // when it comes to optimisations (however, we do not block on
1034 // that here, it's up to the inserter to the list to do so).
1035 // This notbaly has to occur after the OutlinedInfo candidates
1036 // have been extracted so we have an end product that will not
1037 // be implicitly adversely affected by any raises unless
1038 // intentionally appended to the list.
1039 // NOTE: This only does so for ConstantData, it could be extended
1040 // to ConstantExpr's with further effort, however, they should
1041 // largely be folded when they get here. Extending it to runtime
1042 // defined/read+writeable allocation sizes would be non-trivial
1043 // (need to factor in movement of any stores to variables the
1044 // allocation size depends on, as well as the usual loads,
1045 // otherwise it'll yield the wrong result after movement) and
1046 // likely be more suitable as an LLVM optimisation pass.
1049
1050 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1051 [](EmitMetadataErrorKind Kind,
1052 const TargetRegionEntryInfo &EntryInfo) -> void {
1053 errs() << "Error of kind: " << Kind
1054 << " when emitting offload entries and metadata during "
1055 "OMPIRBuilder finalization \n";
1056 };
1057
1058 if (!OffloadInfoManager.empty())
1060
1061 // Rewrite uses of globals to their replacement declare target globals if
1062 // we are processing a device module.
1063 if (Config.isTargetDevice())
1064 applyDeclareTargetGlobalReplacements();
1065
1066 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1067 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1068 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1069 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1070 }
1071
1072 IsFinalized = true;
1073}
1074
1075bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1076
1078 GlobalValue *Original, GlobalValue *Replacement) {
1079 assert(Original && Replacement &&
1080 "Null values provided to registerDeclareTargetGlobalReplacement");
1081 DeclareTargetGlobalReplacements.push_back({Original, Replacement});
1082}
1083
1084void OpenMPIRBuilder::applyDeclareTargetGlobalReplacements() {
1085 for (DeclareTargetGlobalReplacement &R : DeclareTargetGlobalReplacements) {
1086 GlobalValue *OldGV = R.Original;
1087 GlobalValue *NewGV = R.Replacement;
1088
1089 assert(OldGV && NewGV &&
1090 "A null value was inserted into DeclareTargetGlobalReplacements");
1091
1092 // The assert above should catch this case, but this is kept to attempt
1093 // to proceed without issue when asserts are off.
1094 if (!OldGV || !NewGV)
1095 continue;
1096
1097 // The replacement global is a reference pointer that holds the
1098 // address of the device-resident storage. Every use must load the
1099 // reference pointer first and use the loaded address.
1100 //
1101 // Constant expression users (e.g. a constant GEP embedded in another
1102 // global's initializer or in an instruction) cannot have a load inserted
1103 // in place, so first expand any constant-expression users that live inside
1104 // functions into instructions. Any remaining constant users are handled
1105 // via a direct constant rewrite below as we cannot materialize a load
1106 // there.
1107 //
1108 // NOTE: We extend the constant rewrite to module scope, as we replace all
1109 // usages.
1110 if (auto *OldConst = dyn_cast<Constant>(OldGV))
1112 /*RestrictToFunc=*/nullptr,
1113 /*RemoveDeadConstants=*/false);
1114
1115 IRBuilderBase::InsertPointGuard Guard(Builder);
1117 for (User *U : Users) {
1118 auto *Insn = dyn_cast<Instruction>(U);
1119 if (!Insn)
1120 continue;
1121
1122 // A PHI node cannot have a load inserted immediately before it, as PHIs
1123 // must remain grouped at the top of their basic block. So we need to
1124 // make sure any loads we emit are generated in the preceding edge, a
1125 // PHI may reference the global on more than one edge, so every matching
1126 // slot must be handled.
1127 if (auto *PHI = dyn_cast<PHINode>(Insn)) {
1128 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I < E; ++I) {
1129 if (PHI->getIncomingValue(I) != OldGV)
1130 continue;
1131
1132 BasicBlock *IncomingBB = PHI->getIncomingBlock(I);
1133 Builder.SetInsertPoint(IncomingBB->getTerminator());
1134 Builder.SetCurrentDebugLocation(PHI->getDebugLoc());
1135 LoadInst *EdgeLoad = Builder.CreateLoad(NewGV->getType(), NewGV);
1136 PHI->setIncomingValue(I, EdgeLoad);
1137 }
1138 continue;
1139 }
1140
1141 Builder.SetInsertPoint(Insn);
1142 Builder.SetCurrentDebugLocation(Insn->getDebugLoc());
1143 LoadInst *Load = Builder.CreateLoad(NewGV->getType(), NewGV);
1144
1145 // The replacement declare target global lives in the default address
1146 // space, whereas the original global may reside in a non-default
1147 // address space. In that case the initial lowering may have
1148 // emitted an addrspacecast that is no longer valid. Replace the
1149 // whole addrspacecast with the load and erase it rather than
1150 // feeding the load back into the (now pointless) cast.
1151 // NOTE: If we end up with replacement declare target globals in
1152 // non-zero AS's the below will need some minor extensions to have the
1153 // option to alter the address space cast to the new address space where
1154 // required rather than just replacing it.
1155 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Insn)) {
1156 unsigned NewGVAS = NewGV->getType()->getPointerAddressSpace();
1157 assert(NewGVAS == 0 &&
1158 "Non-default address space declare target global");
1159 unsigned OldGVAS = OldGV->getType()->getPointerAddressSpace();
1160 unsigned DestAS = ASC->getType()->getPointerAddressSpace();
1161 if (DestAS == 0 && NewGVAS != OldGVAS) {
1162 ASC->replaceAllUsesWith(Load);
1163 ASC->eraseFromParent();
1164 continue;
1165 }
1166 }
1167
1168 Insn->replaceUsesOfWith(OldGV, Load);
1169 }
1170 }
1171
1173}
1174
1176 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1177}
1178
1180 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1181 auto *GV =
1182 new GlobalVariable(M, I32Ty,
1183 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1184 ConstantInt::get(I32Ty, Value), Name);
1185 GV->setVisibility(GlobalValue::HiddenVisibility);
1186
1187 return GV;
1188}
1189
1191 if (List.empty())
1192 return;
1193
1194 // Convert List to what ConstantArray needs.
1196 UsedArray.resize(List.size());
1197 for (unsigned I = 0, E = List.size(); I != E; ++I)
1199 cast<Constant>(&*List[I]), Builder.getPtrTy());
1200
1201 if (UsedArray.empty())
1202 return;
1203 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1204
1205 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1206 ConstantArray::get(ATy, UsedArray), Name);
1207
1208 GV->setSection("llvm.metadata");
1209}
1210
1213 OMPTgtExecModeFlags Mode) {
1214 auto *Int8Ty = Builder.getInt8Ty();
1215 auto *GVMode = new GlobalVariable(
1216 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1217 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1218 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1219 return GVMode;
1220}
1221
1223 uint32_t SrcLocStrSize,
1224 IdentFlag LocFlags,
1225 unsigned Reserve2Flags) {
1226 // Enable "C-mode".
1227 LocFlags |= OMP_IDENT_FLAG_KMPC;
1228
1229 Constant *&Ident =
1230 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1231 if (!Ident) {
1232 Constant *I32Null = ConstantInt::getNullValue(Int32);
1233 Constant *IdentData[] = {I32Null,
1234 ConstantInt::get(Int32, uint32_t(LocFlags)),
1235 ConstantInt::get(Int32, Reserve2Flags),
1236 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1237
1238 size_t SrcLocStrArgIdx = 4;
1239 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1241 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1242 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1243 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1244 Constant *Initializer =
1245 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1246
1247 // Look for existing encoding of the location + flags, not needed but
1248 // minimizes the difference to the existing solution while we transition.
1249 for (GlobalVariable &GV : M.globals())
1250 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1251 if (GV.getInitializer() == Initializer)
1252 Ident = &GV;
1253
1254 if (!Ident) {
1255 auto *GV = new GlobalVariable(
1256 M, OpenMPIRBuilder::Ident,
1257 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1259 M.getDataLayout().getDefaultGlobalsAddressSpace());
1260 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1261 GV->setAlignment(Align(8));
1262 Ident = GV;
1263 }
1264 }
1265
1266 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1267}
1268
1270 uint32_t &SrcLocStrSize) {
1271 SrcLocStrSize = LocStr.size();
1272 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1273 if (!SrcLocStr) {
1274 Constant *Initializer =
1275 ConstantDataArray::getString(M.getContext(), LocStr);
1276
1277 // Look for existing encoding of the location, not needed but minimizes the
1278 // difference to the existing solution while we transition.
1279 for (GlobalVariable &GV : M.globals())
1280 if (GV.isConstant() && GV.hasInitializer() &&
1281 GV.getInitializer() == Initializer)
1282 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1283
1284 SrcLocStr = Builder.CreateGlobalString(
1285 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1286 &M);
1287 }
1288 return SrcLocStr;
1289}
1290
1292 StringRef FileName,
1293 unsigned Line, unsigned Column,
1294 uint32_t &SrcLocStrSize) {
1295 SmallString<128> Buffer;
1296 Buffer.push_back(';');
1297 Buffer.append(FileName);
1298 Buffer.push_back(';');
1299 Buffer.append(FunctionName);
1300 Buffer.push_back(';');
1301 Buffer.append(std::to_string(Line));
1302 Buffer.push_back(';');
1303 Buffer.append(std::to_string(Column));
1304 Buffer.push_back(';');
1305 Buffer.push_back(';');
1306 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1307}
1308
1309Constant *
1311 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1312 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1313}
1314
1316 uint32_t &SrcLocStrSize,
1317 Function *F) {
1318 DILocation *DIL = DL.get();
1319 if (!DIL)
1320 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1321 StringRef FileName =
1322 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1323 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1324 if (Function.empty() && F)
1325 Function = F->getName();
1326 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1327 DIL->getColumn(), SrcLocStrSize);
1328}
1329
1331 uint32_t &SrcLocStrSize) {
1332 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1333 Loc.IP.getBlock()->getParent());
1334}
1335
1338 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1339 "omp_global_thread_num");
1340}
1341
1342OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1343 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1344 ArrayRef<Type *> ResultPtrTys,
1345 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1346 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1347 "expected one result pointer type per in_reduction item");
1348 if (!updateToLocation(Loc))
1349 return Loc.IP;
1350 if (OrigPtrs.empty())
1351 return Builder.saveIP();
1352
1353 // Compute the executing thread's gtid once for the whole target body and
1354 // reuse it for every in_reduction lookup, so a target with several
1355 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1356 // item.
1357 uint32_t SrcLocStrSize;
1358 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1359 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1360 Value *Gtid = getOrCreateThreadID(Ident);
1361
1362 // The runtime entry point takes (and returns) a generic, default-address-
1363 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1364 // taskgroups to find the matching task_reduction registration for the item.
1365 Type *PtrTy = PointerType::getUnqual(M.getContext());
1366 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1367 FunctionCallee GetThData =
1368 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1369
1370 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1371 // Normalize a non-default-address-space original pointer to the generic
1372 // address space before the call.
1373 Value *OrigPtr = OrigPtrs[Idx];
1374 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1375 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1376 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1377
1378 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1379 "omp.inred.priv");
1380
1381 // Cast the returned private pointer back to the requested address space
1382 // when it differs.
1383 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1384 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1385 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1386
1387 MapPrivateCB(Idx, Priv);
1388 }
1389 return Builder.saveIP();
1390}
1391
1394 bool ForceSimpleCall, bool CheckCancelFlag) {
1395 if (!updateToLocation(Loc))
1396 return Loc.IP;
1397
1398 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1399 // __kmpc_barrier(loc, thread_id);
1400
1401 IdentFlag BarrierLocFlags;
1402 switch (Kind) {
1403 case OMPD_for:
1404 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1405 break;
1406 case OMPD_sections:
1407 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1408 break;
1409 case OMPD_single:
1410 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1411 break;
1412 case OMPD_barrier:
1413 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1414 break;
1415 default:
1416 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1417 break;
1418 }
1419
1420 uint32_t SrcLocStrSize;
1421 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1422 Value *Args[] = {
1423 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1424 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1425
1426 // If we are in a cancellable parallel region, barriers are cancellation
1427 // points.
1428 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1429 bool UseCancelBarrier =
1430 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1431
1433 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1434 ? OMPRTL___kmpc_cancel_barrier
1435 : OMPRTL___kmpc_barrier),
1436 Args);
1437
1438 if (UseCancelBarrier && CheckCancelFlag)
1439 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1440 return Err;
1441
1442 return Builder.saveIP();
1443}
1444
1447 Value *IfCondition,
1448 omp::Directive CanceledDirective) {
1449 if (!updateToLocation(Loc))
1450 return Loc.IP;
1451
1452 // LLVM utilities like blocks with terminators.
1453 auto *UI = Builder.CreateUnreachable();
1454
1455 Instruction *ThenTI = UI, *ElseTI = nullptr;
1456 if (IfCondition) {
1457 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1458
1459 // Even if the if condition evaluates to false, this should count as a
1460 // cancellation point
1461 Builder.SetInsertPoint(ElseTI);
1462 auto ElseIP = Builder.saveIP();
1463
1465 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1466 if (!IPOrErr)
1467 return IPOrErr;
1468 }
1469
1470 Builder.SetInsertPoint(ThenTI);
1471
1472 Value *CancelKind = nullptr;
1473 switch (CanceledDirective) {
1474#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1475 case DirectiveEnum: \
1476 CancelKind = Builder.getInt32(Value); \
1477 break;
1478#include "llvm/Frontend/OpenMP/OMPKinds.def"
1479 default:
1480 llvm_unreachable("Unknown cancel kind!");
1481 }
1482
1483 uint32_t SrcLocStrSize;
1484 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1485 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1486 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1488 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1489
1490 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1491 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1492 return Err;
1493
1494 // Update the insertion point and remove the terminator we introduced.
1495 Builder.SetInsertPoint(UI->getParent());
1496 UI->eraseFromParent();
1497
1498 return Builder.saveIP();
1499}
1500
1503 omp::Directive CanceledDirective) {
1504 if (!updateToLocation(Loc))
1505 return Loc.IP;
1506
1507 // LLVM utilities like blocks with terminators.
1508 auto *UI = Builder.CreateUnreachable();
1509 Builder.SetInsertPoint(UI);
1510
1511 Value *CancelKind = nullptr;
1512 switch (CanceledDirective) {
1513#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1514 case DirectiveEnum: \
1515 CancelKind = Builder.getInt32(Value); \
1516 break;
1517#include "llvm/Frontend/OpenMP/OMPKinds.def"
1518 default:
1519 llvm_unreachable("Unknown cancel kind!");
1520 }
1521
1522 uint32_t SrcLocStrSize;
1523 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1524 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1525 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1527 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1528
1529 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1530 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1531 return Err;
1532
1533 // Update the insertion point and remove the terminator we introduced.
1534 Builder.SetInsertPoint(UI->getParent());
1535 UI->eraseFromParent();
1536
1537 return Builder.saveIP();
1538}
1539
1541 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1542 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1543 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1544 if (!updateToLocation(Loc))
1545 return Loc.IP;
1546
1547 Builder.restoreIP(AllocaIP);
1548 auto *KernelArgsPtr =
1549 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1551
1552 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1553 llvm::Value *Arg =
1554 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1555 Builder.CreateAlignedStore(
1556 KernelArgs[I], Arg,
1557 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1558 }
1559
1560 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1561 NumThreads, HostPtr, KernelArgsPtr};
1562
1564 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1565 OffloadingArgs);
1566
1567 return Builder.saveIP();
1568}
1569
1571 const LocationDescription &Loc, Value *OutlinedFnID,
1572 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1573 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1574
1575 if (!updateToLocation(Loc))
1576 return Loc.IP;
1577
1578 // On top of the arrays that were filled up, the target offloading call
1579 // takes as arguments the device id as well as the host pointer. The host
1580 // pointer is used by the runtime library to identify the current target
1581 // region, so it only has to be unique and not necessarily point to
1582 // anything. It could be the pointer to the outlined function that
1583 // implements the target region, but we aren't using that so that the
1584 // compiler doesn't need to keep that, and could therefore inline the host
1585 // function if proven worthwhile during optimization.
1586
1587 // From this point on, we need to have an ID of the target region defined.
1588 assert(OutlinedFnID && "Invalid outlined function ID!");
1589 (void)OutlinedFnID;
1590
1591 // Return value of the runtime offloading call.
1592 Value *Return = nullptr;
1593
1594 // Arguments for the target kernel.
1595 SmallVector<Value *> ArgsVector;
1596 getKernelArgsVector(Args, Builder, ArgsVector);
1597
1598 // The target region is an outlined function launched by the runtime
1599 // via calls to __tgt_target_kernel().
1600 //
1601 // Note that on the host and CPU targets, the runtime implementation of
1602 // these calls simply call the outlined function without forking threads.
1603 // The outlined functions themselves have runtime calls to
1604 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1605 // the compiler in emitTeamsCall() and emitParallelCall().
1606 //
1607 // In contrast, on the NVPTX target, the implementation of
1608 // __tgt_target_teams() launches a GPU kernel with the requested number
1609 // of teams and threads so no additional calls to the runtime are required.
1610 // Check the error code and execute the host version if required.
1611 Builder.restoreIP(emitTargetKernel(
1612 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1613 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1614
1615 BasicBlock *OffloadFailedBlock =
1616 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1617 BasicBlock *OffloadContBlock =
1618 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1619 Value *Failed = Builder.CreateIsNotNull(Return);
1620 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1621
1622 auto CurFn = Builder.GetInsertBlock()->getParent();
1623 emitBlock(OffloadFailedBlock, CurFn);
1624 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1625 if (!AfterIP)
1626 return AfterIP.takeError();
1627 Builder.restoreIP(*AfterIP);
1628 emitBranch(OffloadContBlock);
1629 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1630 return Builder.saveIP();
1631}
1632
1634 Value *CancelFlag, omp::Directive CanceledDirective) {
1635 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1636 "Unexpected cancellation!");
1637
1638 // For a cancel barrier we create two new blocks.
1639 BasicBlock *BB = Builder.GetInsertBlock();
1640 BasicBlock *NonCancellationBlock;
1641 if (Builder.GetInsertPoint() == BB->end()) {
1642 // TODO: This branch will not be needed once we moved to the
1643 // OpenMPIRBuilder codegen completely.
1644 NonCancellationBlock = BasicBlock::Create(
1645 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1646 } else {
1647 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1649 Builder.SetInsertPoint(BB);
1650 }
1651 BasicBlock *CancellationBlock = BasicBlock::Create(
1652 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1653
1654 // Jump to them based on the return value.
1655 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1656 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1657 /* TODO weight */ nullptr, nullptr);
1658
1659 // From the cancellation block we finalize all variables and go to the
1660 // post finalization block that is known to the FiniCB callback.
1661 auto &FI = FinalizationStack.back();
1662 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1663 if (!FiniBBOrErr)
1664 return FiniBBOrErr.takeError();
1665 Builder.SetInsertPoint(CancellationBlock);
1666 Builder.CreateBr(*FiniBBOrErr);
1667
1668 // The continuation block is where code generation continues.
1669 Builder.SetInsertPoint(NonCancellationBlock, NonCancellationBlock->begin());
1670 return Error::success();
1671}
1672
1673/// Create wrapper function used to gather the outlined function's argument
1674/// structure from a shared buffer and to forward them to it when running in
1675/// Generic mode.
1676///
1677/// The outlined function is expected to receive 2 integer arguments followed by
1678/// an optional pointer argument to an argument structure holding the rest.
1680 Function &OutlinedFn) {
1681 size_t NumArgs = OutlinedFn.arg_size();
1682 assert((NumArgs == 2 || NumArgs == 3) &&
1683 "expected a 2-3 argument parallel outlined function");
1684 bool UseArgStruct = NumArgs == 3;
1685
1686 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1687 IRBuilder<>::InsertPointGuard IPG(Builder);
1688 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1689 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1690 /*isVarArg=*/false);
1691 auto *WrapperFn =
1693 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1694
1695 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1696 WrapperFn->addParamAttr(0, Attribute::ZExt);
1697 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1698
1699 BasicBlock *EntryBB =
1700 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1701 Builder.SetInsertPoint(EntryBB);
1702
1703 // Allocation.
1704 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1705 /*ArraySize=*/nullptr, "addr");
1706 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1707 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1708 AddrAlloca->getName() + ".ascast");
1709
1710 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1711 /*ArraySize=*/nullptr, "zero");
1712 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1713 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1714 ZeroAlloca->getName() + ".ascast");
1715
1716 Value *ArgsAlloca = nullptr;
1717 if (UseArgStruct) {
1718 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1719 /*ArraySize=*/nullptr, "global_args");
1720 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1721 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1722 ArgsAlloca->getName() + ".ascast");
1723 }
1724
1725 // Initialization.
1726 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1727 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1728 if (UseArgStruct) {
1729 Builder.CreateCall(
1730 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1731 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1732 {ArgsAlloca});
1733 }
1734
1735 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1736
1737 // Load structArg from global_args.
1738 if (UseArgStruct) {
1739 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1740 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1741 {Builder.getInt64(0)});
1742 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1743 Args.push_back(StructArg);
1744 }
1745
1746 // Call the outlined function holding the parallel body.
1747 Builder.CreateCall(&OutlinedFn, Args);
1748 Builder.CreateRetVoid();
1749
1750 return WrapperFn;
1751}
1752
1753// Callback used to create OpenMP runtime calls to support
1754// omp parallel clause for the device.
1755// We need to use this callback to replace call to the OutlinedFn in OuterFn
1756// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1758 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1759 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1760 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1761 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1762 assert(OutlinedFn.arg_size() >= 2 &&
1763 "Expected at least tid and bounded tid as arguments");
1764 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1765
1766 // Add some known attributes.
1767 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1768 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1769 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1770 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1771 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1772 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1773
1774 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1775 assert(CI && "Expected call instruction to outlined function");
1776 CI->getParent()->setName("omp_parallel");
1777
1778 Builder.SetInsertPoint(CI);
1779 Type *PtrTy = OMPIRBuilder->VoidPtr;
1780
1781 // Add alloca for kernel args
1782 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1783 Builder.SetInsertPoint(OuterAllocaBB, OuterAllocaBB->getFirstInsertionPt());
1784 AllocaInst *ArgsAlloca =
1785 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1786 Value *Args = ArgsAlloca;
1787 // Add address space cast if array for storing arguments is not allocated
1788 // in address space 0
1789 if (ArgsAlloca->getAddressSpace())
1790 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1791 Builder.restoreIP(CurrentIP);
1792
1793 // Store captured vars which are used by kmpc_parallel_60
1794 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1795 Value *V = *(CI->arg_begin() + 2 + Idx);
1796 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1797 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1798 Builder.CreateStore(V, StoreAddress);
1799 }
1800
1801 Value *Cond =
1802 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1803 : Builder.getInt32(1);
1804 Value *NumThreadsArg =
1805 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1806 : Builder.getInt32(-1);
1807
1808 // If this is not a Generic kernel, we can skip generating the wrapper.
1809 Value *WrapperFn;
1810 if (isGenericKernel(*OuterFn))
1811 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1812 else
1813 WrapperFn = Constant::getNullValue(PtrTy);
1814
1815 // Build kmpc_parallel_60 call
1816 Value *Parallel60CallArgs[] = {
1817 /* identifier*/ Ident,
1818 /* global thread num*/ ThreadID,
1819 /* if expression */ Cond,
1820 /* number of threads */ NumThreadsArg,
1821 /* Proc bind */ Builder.getInt32(-1),
1822 /* outlined function */ &OutlinedFn,
1823 /* wrapper function */ WrapperFn,
1824 /* arguments of the outlined funciton*/ Args,
1825 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1826 /* strict for number of threads */ Builder.getInt32(0)};
1827
1828 FunctionCallee RTLFn =
1829 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1830
1831 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1832
1833 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1834 << *Builder.GetInsertBlock()->getParent() << "\n");
1835
1836 // Initialize the local TID stack location with the argument value.
1837 Builder.SetInsertPoint(PrivTID);
1838 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1839 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1840 PrivTIDAddr);
1841
1842 // Remove redundant call to the outlined function.
1843 CI->eraseFromParent();
1844
1845 for (Instruction *I : ToBeDeleted) {
1846 I->eraseFromParent();
1847 }
1848}
1849
1850// Callback used to create OpenMP runtime calls to support
1851// omp parallel clause for the host.
1852// We need to use this callback to replace call to the OutlinedFn in OuterFn
1853// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1854static void
1856 Function *OuterFn, Value *Ident, Value *IfCondition,
1857 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1858 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1859 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1860 FunctionCallee RTLFn;
1861 if (IfCondition) {
1862 RTLFn =
1863 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1864 } else {
1865 RTLFn =
1866 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1867 }
1868 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1869 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1870 LLVMContext &Ctx = F->getContext();
1871 MDBuilder MDB(Ctx);
1872 // Annotate the callback behavior of the __kmpc_fork_call:
1873 // - The callback callee is argument number 2 (microtask).
1874 // - The first two arguments of the callback callee are unknown (-1).
1875 // - All variadic arguments to the __kmpc_fork_call are passed to the
1876 // callback callee.
1877 F->addMetadata(LLVMContext::MD_callback,
1879 2, {-1, -1},
1880 /* VarArgsArePassed */ true)}));
1881 }
1882 }
1883 // Add some known attributes.
1884 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1885 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1886 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1887
1888 assert(OutlinedFn.arg_size() >= 2 &&
1889 "Expected at least tid and bounded tid as arguments");
1890 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1891
1892 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1893 CI->getParent()->setName("omp_parallel");
1894 Builder.SetInsertPoint(CI);
1895
1896 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1897 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1898 &OutlinedFn};
1899
1900 SmallVector<Value *, 16> RealArgs;
1901 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1902 if (IfCondition) {
1903 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1904 RealArgs.push_back(Cond);
1905 }
1906 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1907
1908 // __kmpc_fork_call_if always expects a void ptr as the last argument
1909 // If there are no arguments, pass a null pointer.
1910 auto PtrTy = OMPIRBuilder->VoidPtr;
1911 if (IfCondition && NumCapturedVars == 0) {
1912 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1913 RealArgs.push_back(NullPtrValue);
1914 }
1915
1916 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1917
1918 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1919 << *Builder.GetInsertBlock()->getParent() << "\n");
1920
1921 // Initialize the local TID stack location with the argument value.
1922 Builder.SetInsertPoint(PrivTID);
1923 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1924 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1925 PrivTIDAddr);
1926
1927 // Remove redundant call to the outlined function.
1928 CI->eraseFromParent();
1929
1930 for (Instruction *I : ToBeDeleted) {
1931 I->eraseFromParent();
1932 }
1933}
1934
1936 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1937 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1938 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1939 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1940 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1941
1942 if (!updateToLocation(Loc))
1943 return Loc.IP;
1944
1945 uint32_t SrcLocStrSize;
1946 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1947 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1948 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1949 (ProcBind != OMP_PROC_BIND_default);
1950 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1951 // If we generate code for the target device, we need to allocate
1952 // struct for aggregate params in the device default alloca address space.
1953 // OpenMP runtime requires that the params of the extracted functions are
1954 // passed as zero address space pointers. This flag ensures that extracted
1955 // function arguments are declared in zero address space
1956 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1957
1958 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1959 // only if we compile for host side.
1960 if (NumThreads && !Config.isTargetDevice()) {
1961 Value *Args[] = {
1962 Ident, ThreadID,
1963 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
1965 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
1966 }
1967
1968 if (ProcBind != OMP_PROC_BIND_default) {
1969 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
1970 Value *Args[] = {
1971 Ident, ThreadID,
1972 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
1974 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
1975 }
1976
1977 BasicBlock *InsertBB = Builder.GetInsertBlock();
1978 Function *OuterFn = InsertBB->getParent();
1979
1980 // Save the outer alloca block because the insertion iterator may get
1981 // invalidated and we still need this later.
1982 BasicBlock *OuterAllocaBlock = OuterAllocIP.getBlock();
1983
1984 // Vector to remember instructions we used only during the modeling but which
1985 // we want to delete at the end.
1987
1988 // Change the location to the outer alloca insertion point to create and
1989 // initialize the allocas we pass into the parallel region.
1990 InsertPointTy NewOuter(OuterAllocaBlock, OuterAllocaBlock->begin());
1991 Builder.restoreIP(NewOuter);
1992 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
1993 AllocaInst *ZeroAddrAlloca =
1994 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
1995 Instruction *TIDAddr = TIDAddrAlloca;
1996 Instruction *ZeroAddr = ZeroAddrAlloca;
1997 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
1998 // Add additional casts to enforce pointers in zero address space
1999 TIDAddr = new AddrSpaceCastInst(
2000 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
2001 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
2002 ToBeDeleted.push_back(TIDAddr);
2003 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2004 PointerType ::get(M.getContext(), 0),
2005 "zero.addr.ascast");
2006 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2007 ToBeDeleted.push_back(ZeroAddr);
2008 }
2009
2010 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2011 // associated arguments in the outlined function, so we delete them later.
2012 ToBeDeleted.push_back(TIDAddrAlloca);
2013 ToBeDeleted.push_back(ZeroAddrAlloca);
2014
2015 // Create an artificial insertion point that will also ensure the blocks we
2016 // are about to split are not degenerated.
2017 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2018
2019 BasicBlock *EntryBB = UI->getParent();
2020 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2021 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2022 BasicBlock *PRegPreFiniBB =
2023 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2024 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2025
2026 auto FiniCBWrapper = [&](InsertPointTy IP) {
2027 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2028 // target to the region exit block.
2029 if (IP.getBlock()->end() == IP.getPoint()) {
2031 Builder.restoreIP(IP);
2032 Instruction *I = Builder.CreateBr(PRegExitBB);
2033 IP = InsertPointTy(I->getParent(), I->getIterator());
2034 }
2035 assert(IP.getBlock()->getTerminator()->getNumSuccessors() == 1 &&
2036 IP.getBlock()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2037 "Unexpected insertion point for finalization call!");
2038 return FiniCB(IP);
2039 };
2040
2041 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2042
2043 // Generate the privatization allocas in the block that will become the entry
2044 // of the outlined function.
2045 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2046 InsertPointTy InnerAllocaIP = Builder.saveIP();
2047
2048 AllocaInst *PrivTIDAddr =
2049 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2050 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2051
2052 // Add some fake uses for OpenMP provided arguments.
2053 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2054 Instruction *ZeroAddrUse =
2055 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2056 ToBeDeleted.push_back(ZeroAddrUse);
2057
2058 // EntryBB
2059 // |
2060 // V
2061 // PRegionEntryBB <- Privatization allocas are placed here.
2062 // |
2063 // V
2064 // PRegionBodyBB <- BodeGen is invoked here.
2065 // |
2066 // V
2067 // PRegPreFiniBB <- The block we will start finalization from.
2068 // |
2069 // V
2070 // PRegionExitBB <- A common exit to simplify block collection.
2071 //
2072
2073 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2074
2075 // Let the caller create the body.
2076 assert(BodyGenCB && "Expected body generation callback!");
2077 InsertPointTy CodeGenIP(PRegBodyBB, PRegBodyBB->begin());
2078 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2079 return Err;
2080
2081 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2082
2083 // If OuterFn is a Generic kernel, we need to use device shared memory to
2084 // allocate argument structures. Otherwise, we use stack allocations as usual.
2085 bool UsesDeviceSharedMemory =
2086 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2087 std::unique_ptr<OutlineInfo> OI =
2088 UsesDeviceSharedMemory
2089 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2090 : std::make_unique<OutlineInfo>();
2091
2092 if (Config.isTargetDevice()) {
2093 // Generate OpenMP target specific runtime call
2094 OI->PostOutlineCB = [=, ToBeDeletedVec =
2095 std::move(ToBeDeleted)](Function &OutlinedFn) {
2096 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2097 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2098 ThreadID, ToBeDeletedVec);
2099 };
2100 } else {
2101 // Generate OpenMP host runtime call
2102 OI->PostOutlineCB = [=, ToBeDeletedVec =
2103 std::move(ToBeDeleted)](Function &OutlinedFn) {
2104 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2105 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2106 };
2107 }
2108
2109 OI->FixUpNonEntryAllocas = true;
2110 OI->OuterAllocBB = OuterAllocaBlock;
2111 OI->EntryBB = PRegEntryBB;
2112 OI->ExitBB = PRegExitBB;
2113 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2114 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2115
2116 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2118 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2119
2120 CodeExtractorAnalysisCache CEAC(*OuterFn);
2121 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2122 /* AggregateArgs */ false,
2123 /* BlockFrequencyInfo */ nullptr,
2124 /* BranchProbabilityInfo */ nullptr,
2125 /* AssumptionCache */ nullptr,
2126 /* AllowVarArgs */ true,
2127 /* AllowAlloca */ true,
2128 /* AllocationBlock */ OuterAllocaBlock,
2129 /* DeallocationBlocks */ {},
2130 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2131
2132 // Find inputs to, outputs from the code region.
2133 BasicBlock *CommonExit = nullptr;
2134 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2135 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2136
2137 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2138 /*CollectGlobalInputs=*/true);
2139
2140 Inputs.remove_if([&](Value *I) {
2142 return GV->getValueType() == OpenMPIRBuilder::Ident;
2143
2144 return false;
2145 });
2146
2147 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2148
2149 FunctionCallee TIDRTLFn =
2150 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2151
2152 auto PrivHelper = [&](Value &V) -> Error {
2153 if (&V == TIDAddr || &V == ZeroAddr) {
2154 OI->ExcludeArgsFromAggregate.push_back(&V);
2155 return Error::success();
2156 }
2157
2159 for (Use &U : V.uses())
2160 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2161 if (ParallelRegionBlockSet.count(UserI->getParent()))
2162 Uses.insert(&U);
2163
2164 // __kmpc_fork_call expects extra arguments as pointers. If the input
2165 // already has a pointer type, everything is fine. Otherwise, store the
2166 // value onto stack and load it back inside the to-be-outlined region. This
2167 // will ensure only the pointer will be passed to the function.
2168 // FIXME: if there are more than 15 trailing arguments, they must be
2169 // additionally packed in a struct.
2170 Value *Inner = &V;
2171 if (!V.getType()->isPointerTy()) {
2173 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2174
2175 Builder.restoreIP(OuterAllocIP);
2176 Value *Ptr;
2177 if (UsesDeviceSharedMemory) {
2178 // Use device shared memory instead, if needed.
2179 Ptr = createOMPAllocShared(OuterAllocIP, V.getType(),
2180 V.getName() + ".reloaded");
2181 for (BasicBlock *DeallocBlock : OuterDeallocBlocks)
2183 InsertPointTy(DeallocBlock, DeallocBlock->getFirstInsertionPt()),
2184 Ptr, V.getType());
2185 } else {
2186 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2187 V.getName() + ".reloaded");
2188 }
2189
2190 // Store to stack at end of the block that currently branches to the entry
2191 // block of the to-be-outlined region.
2192 Builder.SetInsertPoint(InsertBB,
2193 InsertBB->getTerminator()->getIterator());
2194 Builder.CreateStore(&V, Ptr);
2195
2196 // Load back next to allocations in the to-be-outlined region.
2197 Builder.restoreIP(InnerAllocaIP);
2198 Inner = Builder.CreateLoad(V.getType(), Ptr);
2199 }
2200
2201 Value *ReplacementValue = nullptr;
2202 CallInst *CI = dyn_cast<CallInst>(&V);
2203 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2204 ReplacementValue = PrivTID;
2205 } else {
2206 InsertPointOrErrorTy AfterIP =
2207 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2208 if (!AfterIP)
2209 return AfterIP.takeError();
2210 Builder.restoreIP(*AfterIP);
2211 InnerAllocaIP = {
2212 InnerAllocaIP.getBlock(),
2213 InnerAllocaIP.getBlock()->getTerminator()->getIterator()};
2214
2215 assert(ReplacementValue &&
2216 "Expected copy/create callback to set replacement value!");
2217 if (ReplacementValue == &V)
2218 return Error::success();
2219 }
2220
2221 for (Use *UPtr : Uses)
2222 UPtr->set(ReplacementValue);
2223
2224 return Error::success();
2225 };
2226
2227 // Reset the inner alloca insertion as it will be used for loading the values
2228 // wrapped into pointers before passing them into the to-be-outlined region.
2229 // Configure it to insert immediately after the fake use of zero address so
2230 // that they are available in the generated body and so that the
2231 // OpenMP-related values (thread ID and zero address pointers) remain leading
2232 // in the argument list.
2233 InnerAllocaIP = IRBuilder<>::InsertPoint(
2234 ZeroAddrUse->getParent(), ZeroAddrUse->getNextNode()->getIterator());
2235
2236 // Reset the outer alloca insertion point to the entry of the relevant block
2237 // in case it was invalidated.
2238 OuterAllocIP = IRBuilder<>::InsertPoint(
2239 OuterAllocaBlock, OuterAllocaBlock->getFirstInsertionPt());
2240
2241 for (Value *Input : Inputs) {
2242 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2243 if (Error Err = PrivHelper(*Input))
2244 return Err;
2245 }
2246 LLVM_DEBUG({
2247 for (Value *Output : Outputs)
2248 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2249 });
2250 assert(Outputs.empty() &&
2251 "OpenMP outlining should not produce live-out values!");
2252
2253 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2254 LLVM_DEBUG({
2255 for (auto *BB : Blocks)
2256 dbgs() << " PBR: " << BB->getName() << "\n";
2257 });
2258
2259 // Adjust the finalization stack, verify the adjustment, and call the
2260 // finalize function a last time to finalize values between the pre-fini
2261 // block and the exit block if we left the parallel "the normal way".
2262 auto FiniInfo = FinalizationStack.pop_back_val();
2263 (void)FiniInfo;
2264 assert(FiniInfo.DK == OMPD_parallel &&
2265 "Unexpected finalization stack state!");
2266
2267 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2268
2269 InsertPointTy PreFiniIP(PRegPreFiniBB, PRegPreFiniTI->getIterator());
2270 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2271 if (!FiniBBOrErr)
2272 return FiniBBOrErr.takeError();
2273 {
2275 Builder.restoreIP(PreFiniIP);
2276 Builder.CreateBr(*FiniBBOrErr);
2277 // There's currently a branch to omp.par.exit. Delete it. We will get there
2278 // via the fini block
2279 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2280 Term->eraseFromParent();
2281 }
2282
2283 // Register the outlined info.
2284 addOutlineInfo(std::move(OI));
2285
2286 InsertPointTy AfterIP(UI->getParent(), UI->getParent()->end());
2287 UI->eraseFromParent();
2288
2289 return AfterIP;
2290}
2291
2293 // Build call void __kmpc_flush(ident_t *loc)
2294 uint32_t SrcLocStrSize;
2295 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2296 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2297
2299 Args);
2300}
2301
2303 if (!updateToLocation(Loc))
2304 return;
2305 emitFlush(Loc);
2306}
2307
2309 Value *Message) {
2310 if (!updateToLocation(Loc))
2311 return;
2312
2313 // Build call void __kmpc_error(ident_t *loc, int severity,
2314 // const char *message)
2315 uint32_t SrcLocStrSize;
2316 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2317 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2318 // Severity: 1 = warning, 2 = fatal.
2319 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2320 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2321 Value *Args[] = {Ident, Severity, MessageArg};
2322
2324 Args);
2325}
2326
2328 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2329 uint32_t SrcLocStrSize;
2330 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2331 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2332 Constant *I32Null = ConstantInt::getNullValue(Int32);
2333 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2334
2336 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2337}
2338
2344
2346 const DependData &Dep) {
2347 // Store the pointer to the variable
2348 Value *Addr = Builder.CreateStructGEP(
2349 DependInfo, Entry,
2350 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2351 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2352 Builder.CreateStore(DepValPtr, Addr);
2353 // Store the size of the variable
2354 Value *Size = Builder.CreateStructGEP(
2355 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2356 Builder.CreateStore(
2357 ConstantInt::get(SizeTy,
2358 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2359 Size);
2360 // Store the dependency kind
2361 Value *Flags = Builder.CreateStructGEP(
2362 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2363 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2364 static_cast<unsigned int>(Dep.DepKind)),
2365 Flags);
2366}
2367
2368// Processes the dependencies in Dependencies and does the following
2369// - Allocates space on the stack of an array of DependInfo objects
2370// - Populates each DependInfo object with relevant information of
2371// the corresponding dependence.
2372// - All code is inserted in the entry block of the current function.
2374 OpenMPIRBuilder &OMPBuilder,
2376 // Early return if we have no dependencies to process
2377 if (Dependencies.empty())
2378 return nullptr;
2379
2380 // Given a vector of DependData objects, in this function we create an
2381 // array on the stack that holds kmp_depend_info objects corresponding
2382 // to each dependency. This is then passed to the OpenMP runtime.
2383 // For example, if there are 'n' dependencies then the following psedo
2384 // code is generated. Assume the first dependence is on a variable 'a'
2385 //
2386 // \code{c}
2387 // DepArray = alloc(n x sizeof(kmp_depend_info);
2388 // idx = 0;
2389 // DepArray[idx].base_addr = ptrtoint(&a);
2390 // DepArray[idx].len = 8;
2391 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2392 // ++idx;
2393 // DepArray[idx].base_addr = ...;
2394 // \endcode
2395
2396 IRBuilderBase &Builder = OMPBuilder.Builder;
2397 Type *DependInfo = OMPBuilder.DependInfo;
2398
2399 Value *DepArray = nullptr;
2400 OpenMPIRBuilder::InsertPointTy OldIP = Builder.saveIP();
2401 Builder.SetInsertPoint(
2403
2404 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2405 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2406
2407 Builder.restoreIP(OldIP);
2408
2409 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2410 Value *Base =
2411 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2412 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2413 }
2414 return DepArray;
2415}
2416
2418 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2419 // global_tid);
2420 uint32_t SrcLocStrSize;
2421 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2422 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2423 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2424
2425 // Ignore return result until untied tasks are supported.
2427 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2428}
2429
2431 DependenciesInfo Dependencies) {
2432 if (!updateToLocation(Loc))
2433 return;
2434
2435 Value *DepArray = nullptr;
2436 Type *DepArrayTy = nullptr;
2437 Value *NumDeps = nullptr;
2438 if (Dependencies.DepArray) {
2439 DepArray = Dependencies.DepArray;
2440 NumDeps = Dependencies.NumDeps;
2441 } else if (!Dependencies.Deps.empty()) {
2442 InsertPointTy OldIP = Builder.saveIP();
2443 BasicBlock &entryBB =
2444 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2445 Builder.SetInsertPoint(&entryBB, entryBB.getFirstInsertionPt());
2446
2447 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2448 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2449 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2450
2451 Builder.restoreIP(OldIP);
2452 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2453 Value *Base =
2454 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2455 this->emitTaskDependency(Builder, Base, Dep);
2456 }
2457 }
2458
2459 if (DepArray) {
2460 uint32_t SrcLocStrSize;
2461 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2462 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2463 Value *Args[] = {
2464 Ident,
2465 getOrCreateThreadID(Ident),
2466 NumDeps,
2467 DepArray,
2468 ConstantInt::get(Builder.getInt32Ty(), 0),
2470 ConstantInt::get(Builder.getInt32Ty(), false)};
2473 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2474 Args);
2475 } else {
2477 }
2478}
2479
2480/// Create the task duplication function passed to kmpc_taskloop.
2481Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2482 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2483 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2484 if (!DupCB)
2486 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2487
2488 // From OpenMP Runtime p_task_dup_t:
2489 // Routine optionally generated by the compiler for setting the lastprivate
2490 // flag and calling needed constructors for private/firstprivate objects (used
2491 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2492 // lastprivate flag.
2493 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2494
2495 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2496
2497 FunctionType *DupFuncTy = FunctionType::get(
2498 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2499 /*isVarArg=*/false);
2500
2501 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2502 "omp_taskloop_dup", M);
2503 Value *DestTaskArg = DupFunction->getArg(0);
2504 Value *SrcTaskArg = DupFunction->getArg(1);
2505 Value *LastprivateFlagArg = DupFunction->getArg(2);
2506 DestTaskArg->setName("dest_task");
2507 SrcTaskArg->setName("src_task");
2508 LastprivateFlagArg->setName("lastprivate_flag");
2509
2510 IRBuilderBase::InsertPointGuard Guard(Builder);
2511 Builder.SetInsertPoint(
2512 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2513
2514 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2515 Type *TaskWithPrivatesTy =
2516 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2517 Value *TaskPrivates = Builder.CreateGEP(
2518 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2519 Value *ContextPtr = Builder.CreateGEP(
2520 PrivatesTy, TaskPrivates,
2521 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2522 return ContextPtr;
2523 };
2524
2525 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2526 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2527
2528 DestTaskContextPtr->setName("destPtr");
2529 SrcTaskContextPtr->setName("srcPtr");
2530
2531 InsertPointTy AllocaIP(&DupFunction->getEntryBlock(),
2532 DupFunction->getEntryBlock().begin());
2533 InsertPointTy CodeGenIP = Builder.saveIP();
2534 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2535 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2536 if (!AfterIPOrError)
2537 return AfterIPOrError.takeError();
2538 Builder.restoreIP(*AfterIPOrError);
2539
2540 Builder.CreateRetVoid();
2541
2542 return DupFunction;
2543}
2544
2545OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2546 const LocationDescription &Loc, InsertPointTy AllocaIP,
2547 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2548 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2549 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2550 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2551 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2552 Value *TaskContextStructPtrVal) {
2553
2554 if (!updateToLocation(Loc))
2555 return InsertPointTy();
2556
2557 uint32_t SrcLocStrSize;
2558 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2559 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2560
2561 BasicBlock *TaskloopExitBB =
2562 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2563 BasicBlock *TaskloopBodyBB =
2564 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2565 BasicBlock *TaskloopAllocaBB =
2566 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2567
2568 InsertPointTy TaskloopAllocaIP =
2569 InsertPointTy(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2570 InsertPointTy TaskloopBodyIP =
2571 InsertPointTy(TaskloopBodyBB, TaskloopBodyBB->begin());
2572
2573 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2574 return Err;
2575
2576 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2577 if (!result) {
2578 return result.takeError();
2579 }
2580
2581 llvm::CanonicalLoopInfo *CLI = result.get();
2582 auto OI = std::make_unique<OutlineInfo>();
2583 OI->EntryBB = TaskloopAllocaBB;
2584 OI->OuterAllocBB = AllocaIP.getBlock();
2585 OI->ExitBB = TaskloopExitBB;
2586 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2587 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2588
2589 // Add the thread ID argument.
2590 SmallVector<Instruction *> ToBeDeleted;
2591 // dummy instruction to be used as a fake argument
2592 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2593 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2594 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2595 TaskloopAllocaIP, "lb", false, true);
2596 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2597 TaskloopAllocaIP, "ub", false, true);
2598 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2599 TaskloopAllocaIP, "step", false, true);
2600 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2601 // aggregate struct
2602 OI->Inputs.insert(FakeLB);
2603 OI->Inputs.insert(FakeUB);
2604 OI->Inputs.insert(FakeStep);
2605 if (TaskContextStructPtrVal)
2606 OI->Inputs.insert(TaskContextStructPtrVal);
2607 assert(((TaskContextStructPtrVal && DupCB) ||
2608 (!TaskContextStructPtrVal && !DupCB)) &&
2609 "Task context struct ptr and duplication callback must be both set "
2610 "or both null");
2611
2612 // It isn't safe to run the duplication bodygen callback inside the post
2613 // outlining callback so this has to be run now before we know the real task
2614 // shareds structure type.
2615 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2616 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2617 Type *FakeSharedsTy = StructType::get(
2618 Builder.getContext(),
2619 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2620 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2621 FakeSharedsTy,
2622 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2623 if (!TaskDupFnOrErr) {
2624 return TaskDupFnOrErr.takeError();
2625 }
2626 Value *TaskDupFn = *TaskDupFnOrErr;
2627
2628 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2629 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2630 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2631 FakeSharedsTy, Final, Mergeable, Priority,
2632 NumOfCollapseLoops](Function &OutlinedFn) mutable {
2633 // Replace the Stale CI by appropriate RTL function call.
2634 assert(OutlinedFn.hasOneUse() &&
2635 "there must be a single user for the outlined function");
2636 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2637
2638 /* Create the casting for the Bounds Values that can be used when outlining
2639 * to replace the uses of the fakes with real values */
2640 BasicBlock *CodeReplBB = StaleCI->getParent();
2641 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2642 Value *CastedLBVal =
2643 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2644 Value *CastedUBVal =
2645 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2646 Value *CastedStepVal =
2647 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2648
2649 Builder.SetInsertPoint(StaleCI);
2650
2651 // Gather the arguments for emitting the runtime call for
2652 // @__kmpc_omp_task_alloc
2653 Function *TaskAllocFn =
2654 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2655
2656 Value *ThreadID = getOrCreateThreadID(Ident);
2657
2658 if (!NoGroup) {
2659 // Emit runtime call for @__kmpc_taskgroup
2660 Function *TaskgroupFn =
2661 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2662 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2663 }
2664
2665 // `flags` Argument Configuration
2666 // Task is tied if (Flags & 1) == 1.
2667 // Task is untied if (Flags & 1) == 0.
2668 // Task is final if (Flags & 2) == 2.
2669 // Task is not final if (Flags & 2) == 0.
2670 // Task is mergeable if (Flags & 4) == 4.
2671 // Task is not mergeable if (Flags & 4) == 0.
2672 // Task is priority if (Flags & 32) == 32.
2673 // Task is not priority if (Flags & 32) == 0.
2674 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2675 if (Final)
2676 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2677 if (Mergeable)
2678 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2679 if (Priority)
2680 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2681
2682 Value *TaskSize = Builder.getInt64(
2683 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2684
2685 AllocaInst *ArgStructAlloca =
2687 assert(ArgStructAlloca &&
2688 "Unable to find the alloca instruction corresponding to arguments "
2689 "for extracted function");
2690 std::optional<TypeSize> ArgAllocSize =
2691 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2692 assert(ArgAllocSize &&
2693 "Unable to determine size of arguments for extracted function");
2694 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2695
2696 // Emit the @__kmpc_omp_task_alloc runtime call
2697 // The runtime call returns a pointer to an area where the task captured
2698 // variables must be copied before the task is run (TaskData)
2699 CallInst *TaskData = Builder.CreateCall(
2700 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2701 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2702 /*task_func=*/&OutlinedFn});
2703
2704 Value *Shareds = StaleCI->getArgOperand(1);
2705 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2706 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2707 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2708 SharedsSize);
2709 // Get the pointer to loop lb, ub, step from task ptr
2710 // and set up the lowerbound,upperbound and step values
2711 llvm::Value *Lb = Builder.CreateGEP(
2712 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2713
2714 llvm::Value *Ub = Builder.CreateGEP(
2715 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2716
2717 llvm::Value *Step = Builder.CreateGEP(
2718 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2719 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2720
2721 // set up the arguments for emitting kmpc_taskloop runtime call
2722 // setting values for ifval, nogroup, sched, grainsize, task_dup
2723 Value *IfCondVal =
2724 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2725 : Builder.getInt32(1);
2726 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2727 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2728 Value *NoGroupVal = Builder.getInt32(1);
2729 Value *SchedVal = Builder.getInt32(Sched);
2730 Value *GrainSizeVal =
2731 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2732 : Builder.getInt64(0);
2733 Value *TaskDup = TaskDupFn;
2734
2735 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2736 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2737
2738 // taskloop runtime call
2739 Function *TaskloopFn =
2740 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2741 Builder.CreateCall(TaskloopFn, Args);
2742
2743 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2744 // nogroup is not defined
2745 if (!NoGroup) {
2746 Function *EndTaskgroupFn =
2747 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2748 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2749 }
2750
2751 StaleCI->eraseFromParent();
2752
2753 Builder.SetInsertPoint(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2754
2755 LoadInst *SharedsOutlined =
2756 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2757 OutlinedFn.getArg(1)->replaceUsesWithIf(
2758 SharedsOutlined,
2759 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2760
2761 Value *IV = CLI->getIndVar();
2762 Type *IVTy = IV->getType();
2763 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2764
2765 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2766 // UpperBound. These GEP's can be reused for loading the tasks respective
2767 // bounds.
2768 Value *TaskLB = nullptr;
2769 Value *TaskUB = nullptr;
2770 Value *TaskStep = nullptr;
2771 Value *LoadTaskLB = nullptr;
2772 Value *LoadTaskUB = nullptr;
2773 Value *LoadTaskStep = nullptr;
2774 for (Instruction &I : *TaskloopAllocaBB) {
2775 if (I.getOpcode() == Instruction::GetElementPtr) {
2776 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2777 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2778 switch (CI->getZExtValue()) {
2779 case 0:
2780 TaskLB = &I;
2781 break;
2782 case 1:
2783 TaskUB = &I;
2784 break;
2785 case 2:
2786 TaskStep = &I;
2787 break;
2788 }
2789 }
2790 } else if (I.getOpcode() == Instruction::Load) {
2791 LoadInst &Load = cast<LoadInst>(I);
2792 if (Load.getPointerOperand() == TaskLB) {
2793 assert(TaskLB != nullptr && "Expected value for TaskLB");
2794 LoadTaskLB = &I;
2795 } else if (Load.getPointerOperand() == TaskUB) {
2796 assert(TaskUB != nullptr && "Expected value for TaskUB");
2797 LoadTaskUB = &I;
2798 } else if (Load.getPointerOperand() == TaskStep) {
2799 assert(TaskStep != nullptr && "Expected value for TaskStep");
2800 LoadTaskStep = &I;
2801 }
2802 }
2803 }
2804
2805 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2806
2807 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2808 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2809 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2810 Value *TripCountMinusOne = Builder.CreateSDiv(
2811 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2812 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2813 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2814 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2815 // set the trip count in the CLI
2816 CLI->setTripCount(CastedTripCount);
2817
2818 Builder.SetInsertPoint(CLI->getBody(),
2819 CLI->getBody()->getFirstInsertionPt());
2820
2821 if (NumOfCollapseLoops > 1) {
2822 llvm::SmallVector<User *> UsersToReplace;
2823 // When using the collapse clause, the bounds of the loop have to be
2824 // adjusted to properly represent the iterator of the outer loop.
2825 Value *IVPlusTaskLB = Builder.CreateAdd(
2826 CLI->getIndVar(),
2827 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2828 // To ensure every Use is correctly captured, we first want to record
2829 // which users to replace the value in, and then replace the value.
2830 for (auto IVUse = CLI->getIndVar()->uses().begin();
2831 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2832 User *IVUser = IVUse->getUser();
2833 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2834 if (Op->getOpcode() == Instruction::URem ||
2835 Op->getOpcode() == Instruction::UDiv) {
2836 UsersToReplace.push_back(IVUser);
2837 }
2838 }
2839 }
2840 for (User *User : UsersToReplace) {
2841 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2842 }
2843 } else {
2844 // The canonical loop is generated with a fixed lower bound. We need to
2845 // update the index calculation code to use the task's lower bound. The
2846 // generated code looks like this:
2847 // %omp_loop.iv = phi ...
2848 // ...
2849 // %tmp = mul [type] %omp_loop.iv, step
2850 // %user_index = add [type] tmp, lb
2851 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2852 // of the normalised induction variable:
2853 // 1. This one: converting the normalised IV to the user IV
2854 // 2. The increment (add)
2855 // 3. The comparison against the trip count (icmp)
2856 // (1) is the only use that is a mul followed by an add so this cannot
2857 // match other IR.
2858 assert(CLI->getIndVar()->getNumUses() == 3 &&
2859 "Canonical loop should have exactly three uses of the ind var");
2860 for (User *IVUser : CLI->getIndVar()->users()) {
2861 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2862 if (Mul->getOpcode() == Instruction::Mul) {
2863 for (User *MulUser : Mul->users()) {
2864 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2865 if (Add->getOpcode() == Instruction::Add) {
2866 Add->setOperand(1, CastedTaskLB);
2867 }
2868 }
2869 }
2870 }
2871 }
2872 }
2873 }
2874
2875 FakeLB->replaceAllUsesWith(CastedLBVal);
2876 FakeUB->replaceAllUsesWith(CastedUBVal);
2877 FakeStep->replaceAllUsesWith(CastedStepVal);
2878 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2879 I->eraseFromParent();
2880 }
2881 };
2882
2883 addOutlineInfo(std::move(OI));
2884 Builder.SetInsertPoint(TaskloopExitBB, TaskloopExitBB->begin());
2885 return Builder.saveIP();
2886}
2887
2890 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2892 llvm::Type::getInt32Ty(M.getContext()));
2893}
2894
2896 const LocationDescription &Loc, InsertPointTy AllocaIP,
2897 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2898 bool Tied, Value *Final, Value *IfCondition,
2899 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2900 bool Mergeable, Value *EventHandle, Value *Priority) {
2901
2902 if (!updateToLocation(Loc))
2903 return InsertPointTy();
2904
2905 uint32_t SrcLocStrSize;
2906 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2907 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2908 // The current basic block is split into four basic blocks. After outlining,
2909 // they will be mapped as follows:
2910 // ```
2911 // def current_fn() {
2912 // current_basic_block:
2913 // br label %task.exit
2914 // task.exit:
2915 // ; instructions after task
2916 // }
2917 // def outlined_fn() {
2918 // task.alloca:
2919 // br label %task.body
2920 // task.body:
2921 // ret void
2922 // }
2923 // ```
2924 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2925 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2926 BasicBlock *TaskAllocaBB =
2927 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2928
2929 InsertPointTy TaskAllocaIP =
2930 InsertPointTy(TaskAllocaBB, TaskAllocaBB->begin());
2931 InsertPointTy TaskBodyIP = InsertPointTy(TaskBodyBB, TaskBodyBB->begin());
2932 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2933 return Err;
2934
2935 auto OI = std::make_unique<OutlineInfo>();
2936 OI->EntryBB = TaskAllocaBB;
2937 OI->OuterAllocBB = AllocaIP.getBlock();
2938 OI->ExitBB = TaskExitBB;
2939 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2940 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2941
2942 // Add the thread ID argument.
2944 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2945 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2946
2947 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2948 Affinities, Mergeable, Priority, EventHandle,
2949 TaskAllocaBB,
2950 ToBeDeleted](Function &OutlinedFn) mutable {
2951 // Replace the Stale CI by appropriate RTL function call.
2952 assert(OutlinedFn.hasOneUse() &&
2953 "there must be a single user for the outlined function");
2954 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2955
2956 // HasShareds is true if any variables are captured in the outlined region,
2957 // false otherwise.
2958 bool HasShareds = StaleCI->arg_size() > 1;
2959 Builder.SetInsertPoint(StaleCI);
2960
2961 // Gather the arguments for emitting the runtime call for
2962 // @__kmpc_omp_task_alloc
2963 Function *TaskAllocFn =
2964 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2965
2966 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
2967 // call.
2968 Value *ThreadID = getOrCreateThreadID(Ident);
2969
2970 // Argument - `flags`
2971 // Task is tied iff (Flags & 1) == 1.
2972 // Task is untied iff (Flags & 1) == 0.
2973 // Task is final iff (Flags & 2) == 2.
2974 // Task is not final iff (Flags & 2) == 0.
2975 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
2976 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
2977 // Task is detachable iff (Flags & 64) == 64.
2978 // Task is not detachable iff (Flags & 64) == 0.
2979 // Task is priority iff (Flags & 32) == 32.
2980 // Task is not priority iff (Flags & 32) == 0.
2981 // TODO: Handle the other flags.
2982 Value *Flags = Builder.getInt32(Tied);
2983 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
2984 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
2985 if (Final) {
2986 Value *FinalFlag =
2987 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
2988 Flags = Builder.CreateOr(FinalFlag, Flags);
2989 }
2990
2991 if (Mergeable || UseMergedIf0Path)
2992 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2993 if (EventHandle)
2994 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
2995 if (Priority)
2996 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2997
2998 // Argument - `sizeof_kmp_task_t` (TaskSize)
2999 // Tasksize refers to the size in bytes of kmp_task_t data structure
3000 // including private vars accessed in task.
3001 // TODO: add kmp_task_t_with_privates (privates)
3002 Value *TaskSize = Builder.getInt64(
3003 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3004
3005 // Argument - `sizeof_shareds` (SharedsSize)
3006 // SharedsSize refers to the shareds array size in the kmp_task_t data
3007 // structure.
3008 Value *SharedsSize = Builder.getInt64(0);
3009 if (HasShareds) {
3010 AllocaInst *ArgStructAlloca =
3012 assert(ArgStructAlloca &&
3013 "Unable to find the alloca instruction corresponding to arguments "
3014 "for extracted function");
3015 std::optional<TypeSize> ArgAllocSize =
3016 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3017 assert(ArgAllocSize &&
3018 "Unable to determine size of arguments for extracted function");
3019 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3020 }
3021 // Emit the @__kmpc_omp_task_alloc runtime call
3022 // The runtime call returns a pointer to an area where the task captured
3023 // variables must be copied before the task is run (TaskData)
3025 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3026 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3027 /*task_func=*/&OutlinedFn});
3028
3029 if (Affinities.Count && Affinities.Info) {
3031 OMPRTL___kmpc_omp_reg_task_with_affinity);
3032
3033 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3034 Affinities.Count, Affinities.Info});
3035 }
3036
3037 // Emit detach clause initialization.
3038 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3039 // task_descriptor);
3040 if (EventHandle) {
3042 OMPRTL___kmpc_task_allow_completion_event);
3043 llvm::Value *EventVal =
3044 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3045 llvm::Value *EventHandleAddr =
3046 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3047 Builder.getPtrTy(0));
3048 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3049 Builder.CreateStore(EventVal, EventHandleAddr);
3050 }
3051 // Copy the arguments for outlined function
3052 if (HasShareds) {
3053 Value *Shareds = StaleCI->getArgOperand(1);
3054 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3055 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3056 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3057 SharedsSize);
3058 }
3059
3060 if (Priority) {
3061 //
3062 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3063 // we populate the priority information into the "kmp_task_t" here
3064 //
3065 // The struct "kmp_task_t" definition is available in kmp.h
3066 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3067 // data2 is used for priority
3068 //
3069 Type *Int32Ty = Builder.getInt32Ty();
3070 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3071 // kmp_task_t* => { ptr }
3072 Type *TaskPtr = StructType::get(VoidPtr);
3073 Value *TaskGEP =
3074 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3075 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3076 Type *TaskStructType = StructType::get(
3077 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3078 Value *PriorityData = Builder.CreateInBoundsGEP(
3079 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3080 // kmp_cmplrdata_t => { ptr, ptr }
3081 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3082 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3083 PriorityData, {Zero, Zero});
3084 Builder.CreateStore(Priority, CmplrData);
3085 }
3086
3087 Value *DepArray = nullptr;
3088 Value *NumDeps = nullptr;
3089 if (Dependencies.DepArray) {
3090 DepArray = Dependencies.DepArray;
3091 NumDeps = Dependencies.NumDeps;
3092 } else if (!Dependencies.Deps.empty()) {
3093 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3094 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3095 }
3096
3097 // In the presence of the `if` clause, the following IR is generated:
3098 // ...
3099 // %data = call @__kmpc_omp_task_alloc(...)
3100 // br i1 %if_condition, label %then, label %else
3101 // then:
3102 // call @__kmpc_omp_task(...)
3103 // br label %exit
3104 // else:
3105 // ;; Wait for resolution of dependencies, if any, before
3106 // ;; beginning the task
3107 // call @__kmpc_omp_wait_deps(...)
3108 // call @__kmpc_omp_task_begin_if0(...)
3109 // call @outlined_fn(...)
3110 // call @__kmpc_omp_task_complete_if0(...)
3111 // br label %exit
3112 // exit:
3113 // ...
3114 if (IfCondition && !UseMergedIf0Path) {
3115 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3116 // terminator.
3117 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3118 Instruction *IfTerminator =
3119 Builder.GetInsertPoint()->getParent()->getTerminator();
3120 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3121 Builder.SetInsertPoint(IfTerminator);
3122 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3123 &ElseTI);
3124 Builder.SetInsertPoint(ElseTI);
3125
3126 if (DepArray) {
3127 Function *TaskWaitFn =
3128 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3130 TaskWaitFn,
3131 {Ident, ThreadID, NumDeps, DepArray,
3132 ConstantInt::get(Builder.getInt32Ty(), 0),
3134 }
3135 Function *TaskBeginFn =
3136 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3137 Function *TaskCompleteFn =
3138 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3139 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3140 CallInst *CI = nullptr;
3141 if (HasShareds)
3142 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3143 else
3144 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3145 CI->setDebugLoc(StaleCI->getDebugLoc());
3146 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3147 Builder.SetInsertPoint(ThenTI);
3148 }
3149
3150 if (DepArray) {
3151 Function *TaskFn =
3152 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3154 TaskFn,
3155 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3156 ConstantInt::get(Builder.getInt32Ty(), 0),
3158
3159 } else {
3160 // Emit the @__kmpc_omp_task runtime call to spawn the task
3161 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3162 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3163 }
3164
3165 StaleCI->eraseFromParent();
3166
3167 Builder.SetInsertPoint(TaskAllocaBB, TaskAllocaBB->begin());
3168 if (HasShareds) {
3169 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3170 OutlinedFn.getArg(1)->replaceUsesWithIf(
3171 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3172 }
3173
3174 // The insert point may refer to one of the instructions about to be
3175 // deleted. It is not needed anymore so clear it instead of leaving it
3176 // dangling.
3177 Builder.ClearInsertionPoint();
3178 for (Instruction *I : llvm::reverse(ToBeDeleted))
3179 I->eraseFromParent();
3180 };
3181
3182 addOutlineInfo(std::move(OI));
3183 Builder.SetInsertPoint(TaskExitBB, TaskExitBB->begin());
3184
3185 return Builder.saveIP();
3186}
3187
3189 const LocationDescription &Loc, InsertPointTy AllocaIP,
3190 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3191 if (!updateToLocation(Loc))
3192 return InsertPointTy();
3193
3194 uint32_t SrcLocStrSize;
3195 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3196 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3197 Value *ThreadID = getOrCreateThreadID(Ident);
3198
3199 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3200 Function *TaskgroupFn =
3201 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3202 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3203
3204 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3205 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3206 return Err;
3207
3208 Builder.SetInsertPoint(TaskgroupExitBB);
3209 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3210 Function *EndTaskgroupFn =
3211 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3212 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3213
3214 return Builder.saveIP();
3215}
3216
3218 const LocationDescription &Loc, InsertPointTy AllocaIP,
3220 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3221 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3222
3223 if (!updateToLocation(Loc))
3224 return Loc.IP;
3225
3226 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3227
3228 // Each section is emitted as a switch case
3229 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3230 // -> OMP.createSection() which generates the IR for each section
3231 // Iterate through all sections and emit a switch construct:
3232 // switch (IV) {
3233 // case 0:
3234 // <SectionStmt[0]>;
3235 // break;
3236 // ...
3237 // case <NumSection> - 1:
3238 // <SectionStmt[<NumSection> - 1]>;
3239 // break;
3240 // }
3241 // ...
3242 // section_loop.after:
3243 // <FiniCB>;
3244 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3245 Builder.restoreIP(CodeGenIP);
3247 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3248 Function *CurFn = Continue->getParent();
3249 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3250
3251 unsigned CaseNumber = 0;
3252 for (auto SectionCB : SectionCBs) {
3254 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3255 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3256 Builder.SetInsertPoint(CaseBB);
3257 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3258 if (Error Err =
3259 SectionCB(InsertPointTy(),
3260 {CaseEndBr->getParent(), CaseEndBr->getIterator()}, {}))
3261 return Err;
3262 CaseNumber++;
3263 }
3264 // remove the existing terminator from body BB since there can be no
3265 // terminators after switch/case
3266 return Error::success();
3267 };
3268 // Loop body ends here
3269 // LowerBound, UpperBound, and STride for createCanonicalLoop
3270 Type *I32Ty = Type::getInt32Ty(M.getContext());
3271 Value *LB = ConstantInt::get(I32Ty, 0);
3272 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3273 Value *ST = ConstantInt::get(I32Ty, 1);
3275 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3276 if (!LoopInfo)
3277 return LoopInfo.takeError();
3278
3279 InsertPointOrErrorTy WsloopIP =
3280 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3281 WorksharingLoopType::ForStaticLoop, !IsNowait);
3282 if (!WsloopIP)
3283 return WsloopIP.takeError();
3284 InsertPointTy AfterIP = *WsloopIP;
3285
3286 BasicBlock *LoopFini = AfterIP.getBlock()->getSinglePredecessor();
3287 assert(LoopFini && "Bad structure of static workshare loop finalization");
3288
3289 // Apply the finalization callback in LoopAfterBB
3290 auto FiniInfo = FinalizationStack.pop_back_val();
3291 assert(FiniInfo.DK == OMPD_sections &&
3292 "Unexpected finalization stack state!");
3293 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3294 return Err;
3295
3296 return AfterIP;
3297}
3298
3301 BodyGenCallbackTy BodyGenCB,
3302 FinalizeCallbackTy FiniCB) {
3303 if (!updateToLocation(Loc))
3304 return Loc.IP;
3305
3306 auto FiniCBWrapper = [&](InsertPointTy IP) {
3307 if (IP.getBlock()->end() != IP.getPoint())
3308 return FiniCB(IP);
3309 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3310 // will fail because that function requires the Finalization Basic Block to
3311 // have a terminator, which is already removed by EmitOMPRegionBody.
3312 // IP is currently at cancelation block.
3313 // We need to backtrack to the condition block to fetch
3314 // the exit block and create a branch from cancelation
3315 // to exit block.
3317 Builder.restoreIP(IP);
3318 auto *CaseBB = Loc.IP.getBlock();
3319 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3320 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3321 Instruction *I = Builder.CreateBr(ExitBB);
3322 IP = InsertPointTy(I->getParent(), I->getIterator());
3323 return FiniCB(IP);
3324 };
3325
3326 Directive OMPD = Directive::OMPD_sections;
3327 // Since we are using Finalization Callback here, HasFinalize
3328 // and IsCancellable have to be true
3329 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3330 /*Conditional*/ false, /*hasFinalize*/ true,
3331 /*IsCancellable*/ true);
3332}
3333
3339
3340Value *OpenMPIRBuilder::getGPUThreadID() {
3343 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3344 {});
3345}
3346
3347Value *OpenMPIRBuilder::getGPUWarpSize() {
3349 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3350}
3351
3352Value *OpenMPIRBuilder::getNVPTXWarpID() {
3353 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3354 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3355}
3356
3357Value *OpenMPIRBuilder::getNVPTXLaneID() {
3358 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3359 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3360 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3361 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3362 "nvptx_lane_id");
3363}
3364
3365Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3366 Type *ToType) {
3367 Type *FromType = From->getType();
3368 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3369 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3370 assert(FromSize > 0 && "From size must be greater than zero");
3371 assert(ToSize > 0 && "To size must be greater than zero");
3372 if (FromType == ToType)
3373 return From;
3374 if (FromSize == ToSize)
3375 return Builder.CreateBitCast(From, ToType);
3376 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3377 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3378 InsertPointTy SaveIP = Builder.saveIP();
3379 Builder.restoreIP(AllocaIP);
3380 Value *CastItem = Builder.CreateAlloca(ToType);
3381 Builder.restoreIP(SaveIP);
3382
3383 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3384 CastItem, Builder.getPtrTy(0));
3385 Builder.CreateStore(From, ValCastItem);
3386 return Builder.CreateLoad(ToType, CastItem);
3387}
3388
3389Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3390 Value *Element,
3391 Type *ElementType,
3392 Value *Offset) {
3393 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3394 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3395
3396 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3397 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3398 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3399 Value *WarpSize =
3400 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3402 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3403 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3404 Value *WarpSizeCast =
3405 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3406 Value *ShuffleCall =
3407 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3408 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3409 // down to the requested element type, otherwise storing the result would
3410 // write past the end of an element narrower than the shuffle width.
3411 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3412}
3413
3414void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3415 Value *DstAddr, Type *ElemType,
3416 Value *Offset, Type *ReductionArrayTy,
3417 bool IsByRefElem) {
3418 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3419 // Create the loop over the big sized data.
3420 // ptr = (void*)Elem;
3421 // ptrEnd = (void*) Elem + 1;
3422 // Step = 8;
3423 // while (ptr + Step < ptrEnd)
3424 // shuffle((int64_t)*ptr);
3425 // Step = 4;
3426 // while (ptr + Step < ptrEnd)
3427 // shuffle((int32_t)*ptr);
3428 // ...
3429 Type *IndexTy = Builder.getIndexTy(
3430 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3431 Value *ElemPtr = DstAddr;
3432 Value *Ptr = SrcAddr;
3433 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3434 if (Size < IntSize)
3435 continue;
3436 Type *IntType = Builder.getIntNTy(IntSize * 8);
3437 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3438 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3439 Value *SrcAddrGEP =
3440 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3441 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3442 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3443
3444 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3445 if ((Size / IntSize) > 1) {
3446 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3447 SrcAddrGEP, Builder.getPtrTy());
3448 BasicBlock *PreCondBB =
3449 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3450 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3451 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3452 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3453 emitBlock(PreCondBB, CurFunc);
3454 PHINode *PhiSrc =
3455 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3456 PhiSrc->addIncoming(Ptr, CurrentBB);
3457 PHINode *PhiDest =
3458 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3459 PhiDest->addIncoming(ElemPtr, CurrentBB);
3460 Ptr = PhiSrc;
3461 ElemPtr = PhiDest;
3462 Value *PtrDiff = Builder.CreatePtrDiff(
3463 Builder.getInt8Ty(), PtrEnd,
3464 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3465 Builder.CreateCondBr(
3466 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3467 ExitBB);
3468 emitBlock(ThenBB, CurFunc);
3469 Value *Res = createRuntimeShuffleFunction(
3470 AllocaIP,
3471 Builder.CreateAlignedLoad(
3472 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3473 IntType, Offset);
3474 Builder.CreateAlignedStore(Res, ElemPtr,
3475 M.getDataLayout().getPrefTypeAlign(ElemType));
3476 Value *LocalPtr =
3477 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3478 Value *LocalElemPtr =
3479 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3480 PhiSrc->addIncoming(LocalPtr, ThenBB);
3481 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3482 emitBranch(PreCondBB);
3483 emitBlock(ExitBB, CurFunc);
3484 } else {
3485 // The shuffled value comes back as the chunk's integer type, so the
3486 // store covers exactly this chunk regardless of what ElemType is.
3487 Value *Res = createRuntimeShuffleFunction(
3488 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3489 Builder.CreateStore(Res, ElemPtr);
3490 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3491 ElemPtr =
3492 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3493 }
3494 Size = Size % IntSize;
3495 }
3496}
3497
3498Error OpenMPIRBuilder::emitReductionListCopy(
3499 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3500 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3501 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3502 Type *IndexTy = Builder.getIndexTy(
3503 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3504 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3505
3506 // Iterates, element-by-element, through the source Reduce list and
3507 // make a copy.
3508 for (auto En : enumerate(ReductionInfos)) {
3509 const ReductionInfo &RI = En.value();
3510 Value *SrcElementAddr = nullptr;
3511 AllocaInst *DestAlloca = nullptr;
3512 Value *DestElementAddr = nullptr;
3513 Value *DestElementPtrAddr = nullptr;
3514 // Should we shuffle in an element from a remote lane?
3515 bool ShuffleInElement = false;
3516 // Set to true to update the pointer in the dest Reduce list to a
3517 // newly created element.
3518 bool UpdateDestListPtr = false;
3519
3520 // Step 1.1: Get the address for the src element in the Reduce list.
3521 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3522 ReductionArrayTy, SrcBase,
3523 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3524 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3525
3526 // Step 1.2: Create a temporary to store the element in the destination
3527 // Reduce list.
3528 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3529 ReductionArrayTy, DestBase,
3530 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3531 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3532 switch (Action) {
3534 InsertPointTy CurIP = Builder.saveIP();
3535 Builder.restoreIP(AllocaIP);
3536
3537 Type *DestAllocaType =
3538 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3539 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3540 ".omp.reduction.element");
3541 DestAlloca->setAlignment(
3542 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3543 DestElementAddr = DestAlloca;
3544 DestElementAddr =
3545 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3546 DestElementAddr->getName() + ".ascast");
3547 Builder.restoreIP(CurIP);
3548 ShuffleInElement = true;
3549 UpdateDestListPtr = true;
3550 break;
3551 }
3553 DestElementAddr =
3554 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3555 break;
3556 }
3557 }
3558
3559 // Now that all active lanes have read the element in the
3560 // Reduce list, shuffle over the value from the remote lane.
3561 if (ShuffleInElement) {
3562 Type *ShuffleType = RI.ElementType;
3563 Value *ShuffleSrcAddr = SrcElementAddr;
3564 Value *ShuffleDestAddr = DestElementAddr;
3565 AllocaInst *LocalStorage = nullptr;
3566
3567 if (IsByRefElem) {
3568 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3569 assert(RI.ByRefAllocatedType &&
3570 "Expected by-ref allocated type to be set");
3571 // For by-ref reductions, we need to copy from the remote lane the
3572 // actual value of the partial reduction computed by that remote lane;
3573 // rather than, for example, a pointer to that data or, even worse, a
3574 // pointer to the descriptor of the by-ref reduction element.
3575 ShuffleType = RI.ByRefElementType;
3576
3577 if (RI.DataPtrPtrGen) {
3578 // Descriptor-based by-ref: extract data pointer from descriptor.
3579 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3580 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3581
3582 if (!GenResult)
3583 return GenResult.takeError();
3584
3585 ShuffleSrcAddr =
3586 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3587
3588 {
3589 InsertPointTy OldIP = Builder.saveIP();
3590 Builder.restoreIP(AllocaIP);
3591
3592 LocalStorage = Builder.CreateAlloca(ShuffleType);
3593 Builder.restoreIP(OldIP);
3594 ShuffleDestAddr = LocalStorage;
3595 }
3596 } else {
3597 // Non-descriptor by-ref: the pointer already references data
3598 // directly. Shuffle into the destination alloca.
3599 ShuffleDestAddr = DestElementAddr;
3600 }
3601 }
3602
3603 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3604 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3605
3606 if (IsByRefElem && RI.DataPtrPtrGen) {
3607 // Copy descriptor from source and update base_ptr to shuffled data
3608 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3609 DestAlloca, Builder.getPtrTy(), ".ascast");
3610
3611 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3612 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3613 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3614
3615 if (!GenResult)
3616 return GenResult.takeError();
3617 }
3618 } else {
3619 switch (RI.EvaluationKind) {
3620 case EvalKind::Scalar: {
3621 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3622 // Store the source element value to the dest element address.
3623 Builder.CreateStore(Elem, DestElementAddr);
3624 break;
3625 }
3626 case EvalKind::Complex: {
3627 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3628 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3629 Value *SrcReal = Builder.CreateLoad(
3630 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3631 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3632 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3633 Value *SrcImg = Builder.CreateLoad(
3634 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3635
3636 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3637 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3638 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3639 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3640 Builder.CreateStore(SrcReal, DestRealPtr);
3641 Builder.CreateStore(SrcImg, DestImgPtr);
3642 break;
3643 }
3644 case EvalKind::Aggregate: {
3645 Value *SizeVal = Builder.getInt64(
3646 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3647 Builder.CreateMemCpy(
3648 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3649 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3650 SizeVal, false);
3651 break;
3652 }
3653 };
3654 }
3655
3656 // Step 3.1: Modify reference in dest Reduce list as needed.
3657 // Modifying the reference in Reduce list to point to the newly
3658 // created element. The element is live in the current function
3659 // scope and that of functions it invokes (i.e., reduce_function).
3660 // RemoteReduceData[i] = (void*)&RemoteElem
3661 if (UpdateDestListPtr) {
3662 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3663 DestElementAddr, Builder.getPtrTy(),
3664 DestElementAddr->getName() + ".ascast");
3665 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3666 }
3667 }
3668
3669 return Error::success();
3670}
3671
3672Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3673 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3674 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3675 IRBuilder<>::InsertPointGuard IPG(Builder);
3676 LLVMContext &Ctx = M.getContext();
3677 FunctionType *FuncTy = FunctionType::get(
3678 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3679 /* IsVarArg */ false);
3680 Function *WcFunc =
3682 "_omp_reduction_inter_warp_copy_func", &M);
3683 WcFunc->setCallingConv(Config.getRuntimeCC());
3684 WcFunc->setAttributes(FuncAttrs);
3685 WcFunc->addParamAttr(0, Attribute::NoUndef);
3686 WcFunc->addParamAttr(1, Attribute::NoUndef);
3687 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3688 Builder.SetInsertPoint(EntryBB);
3689 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3690
3691 // ReduceList: thread local Reduce list.
3692 // At the stage of the computation when this function is called, partially
3693 // aggregated values reside in the first lane of every active warp.
3694 Argument *ReduceListArg = WcFunc->getArg(0);
3695 // NumWarps: number of warps active in the parallel region. This could
3696 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3697 Argument *NumWarpsArg = WcFunc->getArg(1);
3698
3699 // This array is used as a medium to transfer, one reduce element at a time,
3700 // the data from the first lane of every warp to lanes in the first warp
3701 // in order to perform the final step of a reduction in a parallel region
3702 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3703 // for reduced latency, as well as to have a distinct copy for concurrently
3704 // executing target regions. The array is declared with common linkage so
3705 // as to be shared across compilation units.
3706 StringRef TransferMediumName =
3707 "__openmp_nvptx_data_transfer_temporary_storage";
3708 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3709 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3710 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3711 if (!TransferMedium) {
3712 TransferMedium = new GlobalVariable(
3713 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3714 UndefValue::get(ArrayTy), TransferMediumName,
3715 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3716 /*AddressSpace=*/3);
3717 }
3718
3719 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3720 Value *GPUThreadID = getGPUThreadID();
3721 // nvptx_lane_id = nvptx_id % warpsize
3722 Value *LaneID = getNVPTXLaneID();
3723 // nvptx_warp_id = nvptx_id / warpsize
3724 Value *WarpID = getNVPTXWarpID();
3725
3726 InsertPointTy AllocaIP =
3727 InsertPointTy(Builder.GetInsertBlock(),
3728 Builder.GetInsertBlock()->getFirstInsertionPt());
3729 Type *Arg0Type = ReduceListArg->getType();
3730 Type *Arg1Type = NumWarpsArg->getType();
3731 Builder.restoreIP(AllocaIP);
3732 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3733 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3734 AllocaInst *NumWarpsAlloca =
3735 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3736 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3737 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3738 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3739 NumWarpsAlloca, Builder.getPtrTy(0),
3740 NumWarpsAlloca->getName() + ".ascast");
3741 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3742 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3743 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3744 InsertPointTy CodeGenIP =
3745 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3746 Builder.restoreIP(CodeGenIP);
3747
3748 Value *ReduceList =
3749 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3750
3751 for (auto En : enumerate(ReductionInfos)) {
3752 //
3753 // Warp master copies reduce element to transfer medium in __shared__
3754 // memory.
3755 //
3756 const ReductionInfo &RI = En.value();
3757 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3758 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3759 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3760 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3761 Type *CType = Builder.getIntNTy(TySize * 8);
3762
3763 unsigned NumIters = RealTySize / TySize;
3764 if (NumIters == 0)
3765 continue;
3766 Value *Cnt = nullptr;
3767 Value *CntAddr = nullptr;
3768 BasicBlock *PrecondBB = nullptr;
3769 BasicBlock *ExitBB = nullptr;
3770 if (NumIters > 1) {
3771 CodeGenIP = Builder.saveIP();
3772 Builder.restoreIP(AllocaIP);
3773 CntAddr =
3774 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3775
3776 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3777 CntAddr->getName() + ".ascast");
3778 Builder.restoreIP(CodeGenIP);
3779 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3780 CntAddr,
3781 /*Volatile=*/false);
3782 PrecondBB = BasicBlock::Create(Ctx, "precond");
3783 ExitBB = BasicBlock::Create(Ctx, "exit");
3784 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3785 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3786 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3787 /*Volatile=*/false);
3788 Value *Cmp = Builder.CreateICmpULT(
3789 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3790 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3791 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3792 }
3793
3794 // kmpc_barrier.
3795 InsertPointOrErrorTy BarrierIP1 =
3797 omp::Directive::OMPD_unknown,
3798 /* ForceSimpleCall */ false,
3799 /* CheckCancelFlag */ true);
3800 if (!BarrierIP1)
3801 return BarrierIP1.takeError();
3802 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3803 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3804 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3805
3806 // if (lane_id == 0)
3807 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3808 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3809 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3810
3811 // Reduce element = LocalReduceList[i]
3812 auto *RedListArrayTy =
3813 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3814 Type *IndexTy = Builder.getIndexTy(
3815 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3816 Value *ElemPtrPtr =
3817 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3818 {ConstantInt::get(IndexTy, 0),
3819 ConstantInt::get(IndexTy, En.index())});
3820 // elemptr = ((CopyType*)(elemptrptr)) + I
3821 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3822
3823 if (IsByRefElem && RI.DataPtrPtrGen) {
3824 InsertPointOrErrorTy GenRes =
3825 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3826
3827 if (!GenRes)
3828 return GenRes.takeError();
3829
3830 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3831 }
3832
3833 if (NumIters > 1)
3834 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3835
3836 // Get pointer to location in transfer medium.
3837 // MediumPtr = &medium[warp_id]
3838 Value *MediumPtr = Builder.CreateInBoundsGEP(
3839 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3840 // elem = *elemptr
3841 //*MediumPtr = elem
3842 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3843 // Store the source element value to the dest element address.
3844 Builder.CreateStore(Elem, MediumPtr,
3845 /*IsVolatile*/ true);
3846 Builder.CreateBr(MergeBB);
3847
3848 // else
3849 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3850 Builder.CreateBr(MergeBB);
3851
3852 // endif
3853 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3854 InsertPointOrErrorTy BarrierIP2 =
3856 omp::Directive::OMPD_unknown,
3857 /* ForceSimpleCall */ false,
3858 /* CheckCancelFlag */ true);
3859 if (!BarrierIP2)
3860 return BarrierIP2.takeError();
3861
3862 // Warp 0 copies reduce element from transfer medium
3863 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3864 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3865 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3866
3867 Value *NumWarpsVal =
3868 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3869 // Up to 32 threads in warp 0 are active.
3870 Value *IsActiveThread =
3871 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3872 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3873
3874 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3875
3876 // SecMediumPtr = &medium[tid]
3877 // SrcMediumVal = *SrcMediumPtr
3878 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3879 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3880 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3881 Value *TargetElemPtrPtr =
3882 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3883 {ConstantInt::get(IndexTy, 0),
3884 ConstantInt::get(IndexTy, En.index())});
3885 Value *TargetElemPtrVal =
3886 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3887 Value *TargetElemPtr = TargetElemPtrVal;
3888
3889 if (IsByRefElem && RI.DataPtrPtrGen) {
3890 InsertPointOrErrorTy GenRes =
3891 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3892
3893 if (!GenRes)
3894 return GenRes.takeError();
3895
3896 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3897 }
3898
3899 if (NumIters > 1)
3900 TargetElemPtr =
3901 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3902
3903 // *TargetElemPtr = SrcMediumVal;
3904 Value *SrcMediumValue =
3905 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3906 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3907 Builder.CreateBr(W0MergeBB);
3908
3909 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3910 Builder.CreateBr(W0MergeBB);
3911
3912 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3913
3914 if (NumIters > 1) {
3915 Cnt = Builder.CreateNSWAdd(
3916 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3917 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3918
3919 auto *CurFn = Builder.GetInsertBlock()->getParent();
3920 emitBranch(PrecondBB);
3921 emitBlock(ExitBB, CurFn);
3922 }
3923 RealTySize %= TySize;
3924 }
3925 }
3926
3927 Builder.CreateRetVoid();
3928
3929 return WcFunc;
3930}
3931
3932Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3933 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3934 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3935 LLVMContext &Ctx = M.getContext();
3936 IRBuilder<>::InsertPointGuard IPG(Builder);
3937 FunctionType *FuncTy =
3938 FunctionType::get(Builder.getVoidTy(),
3939 {Builder.getPtrTy(), Builder.getInt16Ty(),
3940 Builder.getInt16Ty(), Builder.getInt16Ty()},
3941 /* IsVarArg */ false);
3942 Function *SarFunc =
3944 "_omp_reduction_shuffle_and_reduce_func", &M);
3945 SarFunc->setCallingConv(Config.getRuntimeCC());
3946 SarFunc->setAttributes(FuncAttrs);
3947 SarFunc->addParamAttr(0, Attribute::NoUndef);
3948 SarFunc->addParamAttr(1, Attribute::NoUndef);
3949 SarFunc->addParamAttr(2, Attribute::NoUndef);
3950 SarFunc->addParamAttr(3, Attribute::NoUndef);
3951 SarFunc->addParamAttr(1, Attribute::SExt);
3952 SarFunc->addParamAttr(2, Attribute::SExt);
3953 SarFunc->addParamAttr(3, Attribute::SExt);
3954 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3955 Builder.SetInsertPoint(EntryBB);
3956 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3957
3958 // Thread local Reduce list used to host the values of data to be reduced.
3959 Argument *ReduceListArg = SarFunc->getArg(0);
3960 // Current lane id; could be logical.
3961 Argument *LaneIDArg = SarFunc->getArg(1);
3962 // Offset of the remote source lane relative to the current lane.
3963 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
3964 // Algorithm version. This is expected to be known at compile time.
3965 Argument *AlgoVerArg = SarFunc->getArg(3);
3966
3967 Type *ReduceListArgType = ReduceListArg->getType();
3968 Type *LaneIDArgType = LaneIDArg->getType();
3969 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
3970 Value *ReduceListAlloca = Builder.CreateAlloca(
3971 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
3972 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3973 LaneIDArg->getName() + ".addr");
3974 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
3975 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
3976 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3977 AlgoVerArg->getName() + ".addr");
3978 ArrayType *RedListArrayTy =
3979 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3980
3981 // Create a local thread-private variable to host the Reduce list
3982 // from a remote lane.
3983 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
3984 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
3985
3986 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3987 ReduceListAlloca, ReduceListArgType,
3988 ReduceListAlloca->getName() + ".ascast");
3989 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3990 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
3991 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3992 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
3993 RemoteLaneOffsetAlloca->getName() + ".ascast");
3994 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3995 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
3996 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3997 RemoteReductionListAlloca, Builder.getPtrTy(),
3998 RemoteReductionListAlloca->getName() + ".ascast");
3999
4000 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
4001 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
4002 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
4003 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
4004
4005 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
4006 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4007 Value *RemoteLaneOffset =
4008 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4009 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4010
4011 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4012
4013 // This loop iterates through the list of reduce elements and copies,
4014 // element by element, from a remote lane in the warp to RemoteReduceList,
4015 // hosted on the thread's stack.
4016 Error EmitRedLsCpRes = emitReductionListCopy(
4017 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4018 ReduceList, RemoteListAddrCast, IsByRef,
4019 {RemoteLaneOffset, nullptr, nullptr});
4020
4021 if (EmitRedLsCpRes)
4022 return EmitRedLsCpRes;
4023
4024 // The actions to be performed on the Remote Reduce list is dependent
4025 // on the algorithm version.
4026 //
4027 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4028 // LaneId % 2 == 0 && Offset > 0):
4029 // do the reduction value aggregation
4030 //
4031 // The thread local variable Reduce list is mutated in place to host the
4032 // reduced data, which is the aggregated value produced from local and
4033 // remote lanes.
4034 //
4035 // Note that AlgoVer is expected to be a constant integer known at compile
4036 // time.
4037 // When AlgoVer==0, the first conjunction evaluates to true, making
4038 // the entire predicate true during compile time.
4039 // When AlgoVer==1, the second conjunction has only the second part to be
4040 // evaluated during runtime. Other conjunctions evaluates to false
4041 // during compile time.
4042 // When AlgoVer==2, the third conjunction has only the second part to be
4043 // evaluated during runtime. Other conjunctions evaluates to false
4044 // during compile time.
4045 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4046 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4047 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4048 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4049 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4050 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4051 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4052 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4053 Value *RemoteOffsetComp =
4054 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4055 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4056 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4057 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4058
4059 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4060 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4061 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4062
4063 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4064 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4065 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4066 ReduceList, Builder.getPtrTy());
4067 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4068 RemoteListAddrCast, Builder.getPtrTy());
4069 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4070 ->addFnAttr(Attribute::NoUnwind);
4071 Builder.CreateBr(MergeBB);
4072
4073 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4074 Builder.CreateBr(MergeBB);
4075
4076 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4077
4078 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4079 // Reduce list.
4080 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4081 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4082 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4083
4084 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4085 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4086 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4087 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4088
4089 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4090
4091 EmitRedLsCpRes = emitReductionListCopy(
4092 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4093 RemoteListAddrCast, ReduceList, IsByRef);
4094
4095 if (EmitRedLsCpRes)
4096 return EmitRedLsCpRes;
4097
4098 Builder.CreateBr(CpyMergeBB);
4099
4100 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4101 Builder.CreateBr(CpyMergeBB);
4102
4103 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4104
4105 Builder.CreateRetVoid();
4106
4107 return SarFunc;
4108}
4109
4111OpenMPIRBuilder::generateReductionDescriptor(
4112 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4113 Type *DescriptorType,
4114 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4115 DataPtrPtrGen) {
4116
4117 // Copy the source descriptor to preserve all metadata (rank, extents,
4118 // strides, etc.)
4119 Value *DescriptorSize =
4120 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4121 Builder.CreateMemCpy(
4122 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4123 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4124 DescriptorSize);
4125
4126 // Update the base pointer field to point to the local shuffled data
4127 Value *DataPtrField;
4128 InsertPointOrErrorTy GenResult =
4129 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4130
4131 if (!GenResult)
4132 return GenResult.takeError();
4133
4134 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4135 DataPtr, Builder.getPtrTy(), ".ascast"),
4136 DataPtrField);
4137
4138 return Builder.saveIP();
4139}
4140
4141Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4142 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4143 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4144 InsertPointTy OldIP = Builder.saveIP();
4145 Builder.restoreIP(AllocaIP);
4146
4147 AllocaInst *DescriptorAlloca =
4148 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4149 DescriptorAlloca->setAlignment(
4150 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4151 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4152 DescriptorAlloca, DescriptorPtrTy,
4153 DescriptorAlloca->getName() + ".ascast");
4154
4155 Builder.restoreIP(OldIP);
4156
4157 InsertPointOrErrorTy GenResult =
4158 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4159 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4160 if (!GenResult)
4161 return GenResult.takeError();
4162
4163 return DescriptorAddr;
4164}
4165
4166Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4167 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4168 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4169 IRBuilder<>::InsertPointGuard IPG(Builder);
4170 LLVMContext &Ctx = M.getContext();
4171 FunctionType *FuncTy = FunctionType::get(
4172 Builder.getVoidTy(),
4173 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4174 /* IsVarArg */ false);
4175 Function *LtGCFunc =
4177 "_omp_reduction_list_to_global_copy_func", &M);
4178 LtGCFunc->setAttributes(FuncAttrs);
4179 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4180 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4181 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4182
4183 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4184 Builder.SetInsertPoint(EntryBlock);
4185 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4186
4187 // Buffer: global reduction buffer.
4188 Argument *BufferArg = LtGCFunc->getArg(0);
4189 // Idx: index of the buffer.
4190 Argument *IdxArg = LtGCFunc->getArg(1);
4191 // ReduceList: thread local Reduce list.
4192 Argument *ReduceListArg = LtGCFunc->getArg(2);
4193
4194 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4195 BufferArg->getName() + ".addr");
4196 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4197 IdxArg->getName() + ".addr");
4198 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4199 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4200 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4201 BufferArgAlloca, Builder.getPtrTy(),
4202 BufferArgAlloca->getName() + ".ascast");
4203 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4204 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4205 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4206 ReduceListArgAlloca, Builder.getPtrTy(),
4207 ReduceListArgAlloca->getName() + ".ascast");
4208
4209 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4210 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4211 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4212
4213 Value *LocalReduceList =
4214 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4215 Value *BufferArgVal =
4216 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4217 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4218 Type *IndexTy = Builder.getIndexTy(
4219 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4220 for (auto En : enumerate(ReductionInfos)) {
4221 const ReductionInfo &RI = En.value();
4222 auto *RedListArrayTy =
4223 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4224 // Reduce element = LocalReduceList[i]
4225 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4226 RedListArrayTy, LocalReduceList,
4227 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4228 // elemptr = ((CopyType*)(elemptrptr)) + I
4229 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4230
4231 // Global = Buffer.VD[Idx];
4232 Value *BufferVD =
4233 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4234 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4235 ReductionsBufferTy, BufferVD, 0, En.index());
4236
4237 switch (RI.EvaluationKind) {
4238 case EvalKind::Scalar: {
4239 Value *TargetElement;
4240
4241 if (IsByRef.empty() || !IsByRef[En.index()]) {
4242 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4243 } else {
4244 if (RI.DataPtrPtrGen) {
4245 InsertPointOrErrorTy GenResult =
4246 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4247
4248 if (!GenResult)
4249 return GenResult.takeError();
4250
4251 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4252 }
4253 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4254 }
4255
4256 Builder.CreateStore(TargetElement, GlobVal);
4257 break;
4258 }
4259 case EvalKind::Complex: {
4260 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4261 RI.ElementType, ElemPtr, 0, 0, ".realp");
4262 Value *SrcReal = Builder.CreateLoad(
4263 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4264 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4265 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4266 Value *SrcImg = Builder.CreateLoad(
4267 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4268
4269 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4270 RI.ElementType, GlobVal, 0, 0, ".realp");
4271 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4272 RI.ElementType, GlobVal, 0, 1, ".imagp");
4273 Builder.CreateStore(SrcReal, DestRealPtr);
4274 Builder.CreateStore(SrcImg, DestImgPtr);
4275 break;
4276 }
4277 case EvalKind::Aggregate: {
4278 Value *SizeVal =
4279 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4280 Builder.CreateMemCpy(
4281 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4282 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4283 break;
4284 }
4285 }
4286 }
4287
4288 Builder.CreateRetVoid();
4289 return LtGCFunc;
4290}
4291
4292Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4293 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4294 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4295 IRBuilder<>::InsertPointGuard IPG(Builder);
4296 LLVMContext &Ctx = M.getContext();
4297 FunctionType *FuncTy = FunctionType::get(
4298 Builder.getVoidTy(),
4299 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4300 /* IsVarArg */ false);
4301 Function *LtGRFunc =
4303 "_omp_reduction_list_to_global_reduce_func", &M);
4304 LtGRFunc->setAttributes(FuncAttrs);
4305 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4306 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4307 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4308
4309 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4310 Builder.SetInsertPoint(EntryBlock);
4311 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4312
4313 // Buffer: global reduction buffer.
4314 Argument *BufferArg = LtGRFunc->getArg(0);
4315 // Idx: index of the buffer.
4316 Argument *IdxArg = LtGRFunc->getArg(1);
4317 // ReduceList: thread local Reduce list.
4318 Argument *ReduceListArg = LtGRFunc->getArg(2);
4319
4320 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4321 BufferArg->getName() + ".addr");
4322 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4323 IdxArg->getName() + ".addr");
4324 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4325 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4326 auto *RedListArrayTy =
4327 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4328
4329 // 1. Build a list of reduction variables.
4330 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4331 Value *LocalReduceList =
4332 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4333
4334 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4335
4336 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4337 BufferArgAlloca, Builder.getPtrTy(),
4338 BufferArgAlloca->getName() + ".ascast");
4339 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4340 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4341 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4342 ReduceListArgAlloca, Builder.getPtrTy(),
4343 ReduceListArgAlloca->getName() + ".ascast");
4344 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4345 LocalReduceList, Builder.getPtrTy(),
4346 LocalReduceList->getName() + ".ascast");
4347
4348 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4349 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4350 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4351
4352 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4353 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4354 Type *IndexTy = Builder.getIndexTy(
4355 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4356 for (auto En : enumerate(ReductionInfos)) {
4357 const ReductionInfo &RI = En.value();
4358
4359 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4360 RedListArrayTy, LocalReduceListAddrCast,
4361 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4362 Value *BufferVD =
4363 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4364 // Global = Buffer.VD[Idx];
4365 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4366 ReductionsBufferTy, BufferVD, 0, En.index());
4367
4368 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4369 // Get source descriptor from the reduce list argument
4370 Value *ReduceList =
4371 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4372 Value *SrcElementPtrPtr =
4373 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4374 {ConstantInt::get(IndexTy, 0),
4375 ConstantInt::get(IndexTy, En.index())});
4376 Value *SrcDescriptorAddr =
4377 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4378
4379 // Copy descriptor from source and update base_ptr to global buffer data
4380 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4381 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4382 if (!ByRefAlloc)
4383 return ByRefAlloc.takeError();
4384
4385 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4386 } else {
4387 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4388 }
4389 }
4390
4391 // Call reduce_function(GlobalReduceList, ReduceList)
4392 Value *ReduceList =
4393 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4394 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4395 ->addFnAttr(Attribute::NoUnwind);
4396 Builder.CreateRetVoid();
4397 return LtGRFunc;
4398}
4399
4400Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4401 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4402 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4403 IRBuilder<>::InsertPointGuard IPG(Builder);
4404 LLVMContext &Ctx = M.getContext();
4405 FunctionType *FuncTy = FunctionType::get(
4406 Builder.getVoidTy(),
4407 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4408 /* IsVarArg */ false);
4409 Function *GtLCFunc =
4411 "_omp_reduction_global_to_list_copy_func", &M);
4412 GtLCFunc->setAttributes(FuncAttrs);
4413 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4414 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4415 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4416
4417 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4418 Builder.SetInsertPoint(EntryBlock);
4419 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4420
4421 // Buffer: global reduction buffer.
4422 Argument *BufferArg = GtLCFunc->getArg(0);
4423 // Idx: index of the buffer.
4424 Argument *IdxArg = GtLCFunc->getArg(1);
4425 // ReduceList: thread local Reduce list.
4426 Argument *ReduceListArg = GtLCFunc->getArg(2);
4427
4428 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4429 BufferArg->getName() + ".addr");
4430 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4431 IdxArg->getName() + ".addr");
4432 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4433 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4434 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4435 BufferArgAlloca, Builder.getPtrTy(),
4436 BufferArgAlloca->getName() + ".ascast");
4437 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4438 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4439 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4440 ReduceListArgAlloca, Builder.getPtrTy(),
4441 ReduceListArgAlloca->getName() + ".ascast");
4442 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4443 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4444 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4445
4446 Value *LocalReduceList =
4447 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4448 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4449 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4450 Type *IndexTy = Builder.getIndexTy(
4451 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4452 for (auto En : enumerate(ReductionInfos)) {
4453 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4454 auto *RedListArrayTy =
4455 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4456 // Reduce element = LocalReduceList[i]
4457 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4458 RedListArrayTy, LocalReduceList,
4459 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4460 // elemptr = ((CopyType*)(elemptrptr)) + I
4461 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4462 // Global = Buffer.VD[Idx];
4463 Value *BufferVD =
4464 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4465 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4466 ReductionsBufferTy, BufferVD, 0, En.index());
4467
4468 switch (RI.EvaluationKind) {
4469 case EvalKind::Scalar: {
4470 Type *ElemType = RI.ElementType;
4471
4472 if (!IsByRef.empty() && IsByRef[En.index()]) {
4473 ElemType = RI.ByRefElementType;
4474 if (RI.DataPtrPtrGen) {
4475 InsertPointOrErrorTy GenResult =
4476 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4477
4478 if (!GenResult)
4479 return GenResult.takeError();
4480
4481 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4482 }
4483 }
4484
4485 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4486 Builder.CreateStore(TargetElement, ElemPtr);
4487 break;
4488 }
4489 case EvalKind::Complex: {
4490 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4491 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4492 Value *SrcReal = Builder.CreateLoad(
4493 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4494 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4495 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4496 Value *SrcImg = Builder.CreateLoad(
4497 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4498
4499 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4500 RI.ElementType, ElemPtr, 0, 0, ".realp");
4501 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4502 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4503 Builder.CreateStore(SrcReal, DestRealPtr);
4504 Builder.CreateStore(SrcImg, DestImgPtr);
4505 break;
4506 }
4507 case EvalKind::Aggregate: {
4508 Value *SizeVal =
4509 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4510 Builder.CreateMemCpy(
4511 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4512 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4513 SizeVal, false);
4514 break;
4515 }
4516 }
4517 }
4518
4519 Builder.CreateRetVoid();
4520 return GtLCFunc;
4521}
4522
4523Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4524 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4525 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4526 IRBuilder<>::InsertPointGuard IPG(Builder);
4527 LLVMContext &Ctx = M.getContext();
4528 auto *FuncTy = FunctionType::get(
4529 Builder.getVoidTy(),
4530 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4531 /* IsVarArg */ false);
4532 Function *GtLRFunc =
4534 "_omp_reduction_global_to_list_reduce_func", &M);
4535 GtLRFunc->setAttributes(FuncAttrs);
4536 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4537 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4538 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4539
4540 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4541 Builder.SetInsertPoint(EntryBlock);
4542 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4543
4544 // Buffer: global reduction buffer.
4545 Argument *BufferArg = GtLRFunc->getArg(0);
4546 // Idx: index of the buffer.
4547 Argument *IdxArg = GtLRFunc->getArg(1);
4548 // ReduceList: thread local Reduce list.
4549 Argument *ReduceListArg = GtLRFunc->getArg(2);
4550
4551 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4552 BufferArg->getName() + ".addr");
4553 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4554 IdxArg->getName() + ".addr");
4555 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4556 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4557 ArrayType *RedListArrayTy =
4558 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4559
4560 // 1. Build a list of reduction variables.
4561 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4562 Value *LocalReduceList =
4563 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4564
4565 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4566
4567 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4568 BufferArgAlloca, Builder.getPtrTy(),
4569 BufferArgAlloca->getName() + ".ascast");
4570 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4571 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4572 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4573 ReduceListArgAlloca, Builder.getPtrTy(),
4574 ReduceListArgAlloca->getName() + ".ascast");
4575 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4576 LocalReduceList, Builder.getPtrTy(),
4577 LocalReduceList->getName() + ".ascast");
4578
4579 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4580 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4581 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4582
4583 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4584 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4585 Type *IndexTy = Builder.getIndexTy(
4586 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4587 for (auto En : enumerate(ReductionInfos)) {
4588 const ReductionInfo &RI = En.value();
4589
4590 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4591 RedListArrayTy, ReductionList,
4592 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4593 // Global = Buffer.VD[Idx];
4594 Value *BufferVD =
4595 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4596 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4597 ReductionsBufferTy, BufferVD, 0, En.index());
4598
4599 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4600 // Get source descriptor from the reduce list
4601 Value *ReduceListVal =
4602 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4603 Value *SrcElementPtrPtr =
4604 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4605 {ConstantInt::get(IndexTy, 0),
4606 ConstantInt::get(IndexTy, En.index())});
4607 Value *SrcDescriptorAddr =
4608 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4609
4610 // Copy descriptor from source and update base_ptr to global buffer data
4611 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4612 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4613 if (!ByRefAlloc)
4614 return ByRefAlloc.takeError();
4615
4616 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4617 } else {
4618 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4619 }
4620 }
4621
4622 // Call reduce_function(ReduceList, GlobalReduceList)
4623 Value *ReduceList =
4624 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4625 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4626 ->addFnAttr(Attribute::NoUnwind);
4627 Builder.CreateRetVoid();
4628 return GtLRFunc;
4629}
4630
4631std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4632 std::string Suffix =
4633 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4634 return (Name + Suffix).str();
4635}
4636
4637Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4638 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4640 AttributeList FuncAttrs) {
4641 IRBuilder<>::InsertPointGuard IPG(Builder);
4642 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4643 {Builder.getPtrTy(), Builder.getPtrTy()},
4644 /* IsVarArg */ false);
4645 std::string Name = getReductionFuncName(ReducerName);
4646 Function *ReductionFunc =
4648 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4649 ReductionFunc->setAttributes(FuncAttrs);
4650 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4651 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4652 BasicBlock *EntryBB =
4653 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4654 Builder.SetInsertPoint(EntryBB);
4655 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4656
4657 // Need to alloca memory here and deal with the pointers before getting
4658 // LHS/RHS pointers out
4659 Value *LHSArrayPtr = nullptr;
4660 Value *RHSArrayPtr = nullptr;
4661 Argument *Arg0 = ReductionFunc->getArg(0);
4662 Argument *Arg1 = ReductionFunc->getArg(1);
4663 Type *Arg0Type = Arg0->getType();
4664 Type *Arg1Type = Arg1->getType();
4665
4666 Value *LHSAlloca =
4667 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4668 Value *RHSAlloca =
4669 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4670 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4671 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4672 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4673 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4674 Builder.CreateStore(Arg0, LHSAddrCast);
4675 Builder.CreateStore(Arg1, RHSAddrCast);
4676 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4677 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4678
4679 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4680 Type *IndexTy = Builder.getIndexTy(
4681 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4682 SmallVector<Value *> LHSPtrs, RHSPtrs;
4683 for (auto En : enumerate(ReductionInfos)) {
4684 const ReductionInfo &RI = En.value();
4685 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4686 RedArrayTy, RHSArrayPtr,
4687 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4688 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4689 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4690 RHSI8Ptr, RI.PrivateVariable->getType(),
4691 RHSI8Ptr->getName() + ".ascast");
4692
4693 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4694 RedArrayTy, LHSArrayPtr,
4695 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4696 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4697 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4698 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4699
4701 LHSPtrs.emplace_back(LHSPtr);
4702 RHSPtrs.emplace_back(RHSPtr);
4703 } else {
4704 Value *LHS = LHSPtr;
4705 Value *RHS = RHSPtr;
4706
4707 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4708 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4709 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4710 }
4711
4712 Value *Reduced;
4713 InsertPointOrErrorTy AfterIP =
4714 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4715 if (!AfterIP)
4716 return AfterIP.takeError();
4717 if (!Builder.GetInsertBlock())
4718 return ReductionFunc;
4719
4720 Builder.restoreIP(*AfterIP);
4721
4722 if (!IsByRef.empty() && !IsByRef[En.index()])
4723 Builder.CreateStore(Reduced, LHSPtr);
4724 }
4725 }
4726
4728 for (auto En : enumerate(ReductionInfos)) {
4729 unsigned Index = En.index();
4730 const ReductionInfo &RI = En.value();
4731 Value *LHSFixupPtr, *RHSFixupPtr;
4732 Builder.restoreIP(RI.ReductionGenClang(
4733 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4734
4735 // Fix the CallBack code genereated to use the correct Values for the LHS
4736 // and RHS
4737 LHSFixupPtr->replaceUsesWithIf(
4738 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4739 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4740 ReductionFunc;
4741 });
4742 RHSFixupPtr->replaceUsesWithIf(
4743 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4744 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4745 ReductionFunc;
4746 });
4747 }
4748
4749 Builder.CreateRetVoid();
4750 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4751 // to the entry block (this is dones for higher opt levels by later passes in
4752 // the pipeline). This has caused issues because non-entry `alloca`s force the
4753 // function to use dynamic stack allocations and we might run out of scratch
4754 // memory.
4755 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4756
4757 return ReductionFunc;
4758}
4759
4760static void
4762 bool IsGPU) {
4763 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4764 (void)RI;
4765 assert(RI.Variable && "expected non-null variable");
4766 assert(RI.PrivateVariable && "expected non-null private variable");
4767 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4768 "expected non-null reduction generator callback");
4769 if (!IsGPU) {
4770 assert(
4771 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4772 "expected variables and their private equivalents to have the same "
4773 "type");
4774 }
4775 assert(RI.Variable->getType()->isPointerTy() &&
4776 "expected variables to be pointers");
4777 }
4778}
4779
4780// The atomic cross-team reduction fast path applies when every reduction in the
4781// set can be represented by an atomicrmw. Clang only populates it for scalar
4782// reductions with a supported atomic operator.
4785 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4786 return static_cast<bool>(RI.AtomicReductionGen);
4787 });
4788}
4789
4791 const LocationDescription &Loc, InsertPointTy AllocaIP,
4792 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4793 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4794 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4795 Value *SrcLocInfo) {
4796 if (!updateToLocation(Loc))
4797 return InsertPointTy();
4798 Builder.restoreIP(CodeGenIP);
4799 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4800 LLVMContext &Ctx = M.getContext();
4801
4802 // Source location for the ident struct
4803 if (!SrcLocInfo) {
4804 uint32_t SrcLocStrSize;
4805 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4806 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4807 }
4808
4809 if (ReductionInfos.size() == 0)
4810 return Builder.saveIP();
4811
4812 BasicBlock *ContinuationBlock = nullptr;
4814 // Copied code from createReductions
4815 BasicBlock *InsertBlock = Loc.IP.getBlock();
4816 ContinuationBlock =
4817 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
4818 InsertBlock->getTerminator()->eraseFromParent();
4819 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
4820 }
4821
4822 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4823 AttributeList FuncAttrs;
4824 AttrBuilder AttrBldr(Ctx);
4825 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4826 AttrBldr.addAttribute(Attr);
4827 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4828 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4829
4830 CodeGenIP = Builder.saveIP();
4831 Expected<Function *> ReductionResult = createReductionFunction(
4832 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4833 ReductionGenCBKind, FuncAttrs);
4834 if (!ReductionResult)
4835 return ReductionResult.takeError();
4836 Function *ReductionFunc = *ReductionResult;
4837 Builder.restoreIP(CodeGenIP);
4838
4839 // Set the grid value in the config needed for lowering later on
4840 if (GridValue.has_value())
4841 Config.setGridValue(GridValue.value());
4842 else
4843 Config.setGridValue(getGridValue(T, ReductionFunc));
4844
4845 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4846 // RedList, shuffle_reduce_func, interwarp_copy_func);
4847 // or
4848 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4849 Value *Res;
4850
4851 // 1. Build a list of reduction variables.
4852 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4853 auto Size = ReductionInfos.size();
4854 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4855 Type *FuncPtrTy =
4856 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4857 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4858 CodeGenIP = Builder.saveIP();
4859 Builder.restoreIP(AllocaIP);
4860 Value *ReductionListAlloca =
4861 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4862 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4863 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4864 Builder.restoreIP(CodeGenIP);
4865 Type *IndexTy = Builder.getIndexTy(
4866 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4867 for (auto En : enumerate(ReductionInfos)) {
4868 const ReductionInfo &RI = En.value();
4869 Value *ElemPtr = Builder.CreateInBoundsGEP(
4870 RedArrayTy, ReductionList,
4871 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4872
4873 Value *PrivateVar = RI.PrivateVariable;
4874 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4875 if (IsByRefElem)
4876 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4877
4878 Value *CastElem =
4879 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4880 Builder.CreateStore(CastElem, ElemPtr);
4881 }
4882 CodeGenIP = Builder.saveIP();
4883 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4884 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4885
4886 if (!SarFunc)
4887 return SarFunc.takeError();
4888
4889 Expected<Function *> CopyResult =
4890 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4891 if (!CopyResult)
4892 return CopyResult.takeError();
4893 Function *WcFunc = *CopyResult;
4894 Builder.restoreIP(CodeGenIP);
4895
4896 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4897
4898 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4899 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4900 // not currently use it. It is computed here conservatively as max(element
4901 // sizes) * N rather than the exact sum, which over-calculates the size for
4902 // mixed reduction types but is harmless given the argument is unused.
4903 // TODO: Consider dropping this computation if the runtime API is ever revised
4904 // to remove the unused parameter.
4905 unsigned MaxDataSize = 0;
4906 SmallVector<Type *> ReductionTypeArgs;
4907 for (auto En : enumerate(ReductionInfos)) {
4908 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4909 // the actual data size stored in the global reduction buffer, consistent
4910 // with the ReductionsBufferTy struct used for GEP offsets below.
4911 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4912 ? En.value().ByRefElementType
4913 : En.value().ElementType;
4914 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4915 if (Size > MaxDataSize)
4916 MaxDataSize = Size;
4917 ReductionTypeArgs.emplace_back(RedTypeArg);
4918 }
4919 Value *ReductionDataSize =
4920 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4921
4922 // Helper function to copy thread-local data back to the original reduction
4923 // list.
4924 Function *CopyScratchToListFunc = nullptr;
4925 // Thread-local storage for the reduction variables.
4926 Value *ScratchForCopyBack = nullptr;
4927 // RL pointer to which the final value from the per-thread scratch should be
4928 // copied back. (Basically RL, appropriately casted if necessary.)
4929 Value *RLForCopyBack = RL;
4930
4931 bool IsAtomicReduction =
4932 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4933
4934 if (!IsTeamsReduction) {
4935 Value *SarFuncCast =
4936 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4937 Value *WcFuncCast =
4938 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4939 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4940 WcFuncCast};
4942 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4943 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4944 } else if (IsAtomicReduction) {
4945 // Atomic cross-team reduction fast path: determine the team's main thread
4946 // that is later to fold its value atomically into the mapped variable.
4947 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4948 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4949 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4950 } else {
4951 CodeGenIP = Builder.saveIP();
4952 StructType *ReductionsBufferTy = StructType::create(
4953 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4954
4955 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4956 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4957 if (!LtGCFunc)
4958 return LtGCFunc.takeError();
4959
4960 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
4961 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4962 if (!GtLCFunc)
4963 return GtLCFunc.takeError();
4964
4965 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
4966 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
4967 if (!GtLRFunc)
4968 return GtLRFunc.takeError();
4969
4970 Builder.restoreIP(CodeGenIP);
4971
4972 // The runtime's cross-team final aggregate uses the storage pointed at by
4973 // its reduce-list argument as per-thread scratch. When the surrounding
4974 // kernel is already in SPMD execution mode, clang emitted each reduction
4975 // private as a per-thread `alloca addrspace(5)`, so the original red_list
4976 // (RL) is already per-thread and nothing else is needed.
4977 //
4978 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
4979 // Generic-mode globalization put the reduction private into team-shared
4980 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
4981 // point all threads of the last team would race on the shared LDS slot.
4982 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
4983 // value in, and hand the per-thread RL to the runtime instead. The writer
4984 // thread copies the final value from that per-thread scratch back to RL
4985 // before running the existing combine path below.
4986
4987 // Thread-local RL (might need localization below before being passed to the
4988 // runtime).
4989 Value *RuntimeRL = RL;
4990
4991 if (!IsSPMD) {
4992 CodeGenIP = Builder.saveIP();
4993 Builder.restoreIP(AllocaIP);
4994 // Allocate thread-local buffer for the reduction variables.
4995 Value *PerThreadScratchAlloca = Builder.CreateAlloca(
4996 ReductionsBufferTy, /*ArraySize=*/nullptr, ".omp.reduction.scratch");
4997 Value *PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
4998 PerThreadScratchAlloca, PtrTy,
4999 PerThreadScratchAlloca->getName() + ".ascast");
5000 // Allocate thread-local buffer for the pointers to the reduction
5001 // variables.
5002 Value *PerThreadRedListAlloca =
5003 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
5004 ".omp.reduction.per_thread_red_list");
5005 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
5006 PerThreadRedListAlloca, PtrTy,
5007 PerThreadRedListAlloca->getName() + ".ascast");
5008 Builder.restoreIP(CodeGenIP);
5009
5010 // Iterate over the reduction variables and copy the team-local value to
5011 // the thread-local buffer.
5012 for (auto En : enumerate(ReductionInfos)) {
5013 const ReductionInfo &RI = En.value();
5014 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5015
5016 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5017 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5018 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5019 0, En.index());
5020
5021 Value *RuntimeListEntry = FieldPtr;
5022 if (IsByRefElem && RI.DataPtrPtrGen) {
5023 Value *SrcDescriptor =
5024 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5025 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5026 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5027 if (!Descriptor)
5028 return Descriptor.takeError();
5029 RuntimeListEntry = *Descriptor;
5030 }
5031 Builder.CreateStore(RuntimeListEntry, Slot);
5032 }
5033 // The copy helpers were emitted with default-AS (AS 0) pointer params
5034 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5035 // but PerThreadScratch and RL live in the target's default AS, which
5036 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5037 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5038 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5039 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5040 PerThreadScratch, CopyArg0Ty);
5041 RLForCopyBack =
5042 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5043 // Use index 0 because there is no array of target values to index into,
5044 // there is only one thread-local memory slot.
5045 // restoreIP above left a stale/empty debug location; this inlinable call
5046 // to a debug-info-bearing helper needs one or the verifier rejects the
5047 // module ("!dbg attachment points at wrong subprogram") after inlining.
5048 Builder.SetCurrentDebugLocation(Loc.DL);
5049 Builder.CreateCall(
5050 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5051 CopyScratchToListFunc = *GtLCFunc;
5052 }
5053
5054 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5055 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5056
5057 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5058 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5059 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5060 }
5061
5062 // 5. Build if (res == 1)
5063 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5064 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5065 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5066 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5067
5068 // 6. Build then branch: where we have reduced values in the master
5069 // thread in each team.
5070 // __kmpc_end_reduce{_nowait}(<gtid>);
5071 // break;
5072 emitBlock(ThenBB, CurFunc);
5073
5074 // Copy the writer thread's per-thread scratch result back into the original
5075 // red-list storage before the existing combine path reads RI.PrivateVariable.
5076 // Set a debug location: this inlinable call to a debug-info-bearing helper
5077 // needs one or the verifier rejects the module after inlining.
5078 if (ScratchForCopyBack) {
5079 Builder.SetCurrentDebugLocation(Loc.DL);
5080 Builder.CreateCall(
5081 CopyScratchToListFunc,
5082 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5083 }
5084
5085 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5086 for (auto En : enumerate(ReductionInfos)) {
5087 const ReductionInfo &RI = En.value();
5088
5089 // Atomic cross-team fast path: each team's main thread folds its
5090 // team-reduced value directly into the mapped reduction variable with a
5091 // single atomicrmw.
5092 if (IsAtomicReduction) {
5094 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5095 if (!AfterIP)
5096 return AfterIP.takeError();
5097 Builder.restoreIP(*AfterIP);
5098 continue;
5099 }
5100
5102 Value *RedValue = RI.Variable;
5103
5104 Value *RHS =
5105 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5106
5108 Value *LHSPtr, *RHSPtr;
5109 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5110 &LHSPtr, &RHSPtr, CurFunc));
5111
5112 // Fix the CallBack code genereated to use the correct Values for the LHS
5113 // and RHS. Cast to match types before replacing (necessary to handle
5114 // different address spaces).
5115 if (LHSPtr->getType() != RedValue->getType())
5116 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5117 RedValue, LHSPtr->getType());
5118 if (RHSPtr->getType() != RHS->getType())
5119 RHS =
5120 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5121
5122 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5123 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5124 ReductionFunc;
5125 });
5126 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5127 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5128 ReductionFunc;
5129 });
5130 } else {
5131 if (IsByRef.empty() || !IsByRef[En.index()]) {
5132 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5133 "red.value." + Twine(En.index()));
5134 }
5135 Value *PrivateRedValue = Builder.CreateLoad(
5136 ValueType, RHS, "red.private.value" + Twine(En.index()));
5137 Value *Reduced;
5138 InsertPointOrErrorTy AfterIP =
5139 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5140 if (!AfterIP)
5141 return AfterIP.takeError();
5142 Builder.restoreIP(*AfterIP);
5143
5144 if (!IsByRef.empty() && !IsByRef[En.index()])
5145 Builder.CreateStore(Reduced, RI.Variable);
5146 }
5147 }
5148 emitBlock(ExitBB, CurFunc);
5149 if (ContinuationBlock) {
5150 Builder.CreateBr(ContinuationBlock);
5151 Builder.SetInsertPoint(ContinuationBlock);
5152 }
5153 Config.setEmitLLVMUsed();
5154
5155 return Builder.saveIP();
5156}
5157
5159 Type *VoidTy = Type::getVoidTy(M.getContext());
5160 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5161 auto *FuncTy =
5162 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5164 ".omp.reduction.func", &M);
5165}
5166
5168 Function *ReductionFunc,
5170 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5171 IRBuilder<>::InsertPointGuard IPG(Builder);
5172 Module *Module = ReductionFunc->getParent();
5173 BasicBlock *ReductionFuncBlock =
5174 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5175 Builder.SetInsertPoint(ReductionFuncBlock);
5176 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5177 Value *LHSArrayPtr = nullptr;
5178 Value *RHSArrayPtr = nullptr;
5179 if (IsGPU) {
5180 // Need to alloca memory here and deal with the pointers before getting
5181 // LHS/RHS pointers out
5182 //
5183 Argument *Arg0 = ReductionFunc->getArg(0);
5184 Argument *Arg1 = ReductionFunc->getArg(1);
5185 Type *Arg0Type = Arg0->getType();
5186 Type *Arg1Type = Arg1->getType();
5187
5188 Value *LHSAlloca =
5189 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5190 Value *RHSAlloca =
5191 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5192 Value *LHSAddrCast =
5193 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5194 Value *RHSAddrCast =
5195 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5196 Builder.CreateStore(Arg0, LHSAddrCast);
5197 Builder.CreateStore(Arg1, RHSAddrCast);
5198 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5199 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5200 } else {
5201 LHSArrayPtr = ReductionFunc->getArg(0);
5202 RHSArrayPtr = ReductionFunc->getArg(1);
5203 }
5204
5205 unsigned NumReductions = ReductionInfos.size();
5206 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5207
5208 for (auto En : enumerate(ReductionInfos)) {
5209 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5210 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5211 RedArrayTy, LHSArrayPtr, 0, En.index());
5212 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5213 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5214 LHSI8Ptr, RI.Variable->getType());
5215 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5216 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5217 RedArrayTy, RHSArrayPtr, 0, En.index());
5218 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5219 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5220 RHSI8Ptr, RI.PrivateVariable->getType());
5221 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5222 Value *Reduced;
5224 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5225 if (!AfterIP)
5226 return AfterIP.takeError();
5227
5228 Builder.restoreIP(*AfterIP);
5229 // TODO: Consider flagging an error.
5230 if (!Builder.GetInsertBlock())
5231 return Error::success();
5232
5233 // store is inside of the reduction region when using by-ref
5234 if (!IsByRef[En.index()])
5235 Builder.CreateStore(Reduced, LHSPtr);
5236 }
5237 Builder.CreateRetVoid();
5238 return Error::success();
5239}
5240
5242 const LocationDescription &Loc, InsertPointTy AllocaIP,
5243 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5244 bool IsNoWait, bool IsTeamsReduction) {
5245 assert(ReductionInfos.size() == IsByRef.size());
5246 if (Config.isGPU())
5247 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5248 IsByRef, IsNoWait, IsTeamsReduction);
5249
5250 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5251
5252 if (!updateToLocation(Loc))
5253 return InsertPointTy();
5254
5255 if (ReductionInfos.size() == 0)
5256 return Builder.saveIP();
5257
5258 BasicBlock *InsertBlock = Loc.IP.getBlock();
5259 BasicBlock *ContinuationBlock =
5260 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
5261 InsertBlock->getTerminator()->eraseFromParent();
5262
5263 // Create and populate array of type-erased pointers to private reduction
5264 // values.
5265 unsigned NumReductions = ReductionInfos.size();
5266 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5267 Builder.SetInsertPoint(AllocaIP.getBlock()->getTerminator());
5268 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5269
5270 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
5271 // Emitting the alloca moved the insertion point into the alloca block and
5272 // can clear the debug loc. Restore back to Loc.DL.
5273 Builder.SetCurrentDebugLocation(Loc.DL);
5274
5275 for (auto En : enumerate(ReductionInfos)) {
5276 unsigned Index = En.index();
5277 const ReductionInfo &RI = En.value();
5278 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5279 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5280 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5281 }
5282
5283 // Emit a call to the runtime function that orchestrates the reduction.
5284 // Declare the reduction function in the process.
5285 Type *IndexTy = Builder.getIndexTy(
5286 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5287 Function *Func = Builder.GetInsertBlock()->getParent();
5288 Module *Module = Func->getParent();
5289 uint32_t SrcLocStrSize;
5290 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5291 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5292 return RI.AtomicReductionGen;
5293 });
5294 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5295 CanGenerateAtomic
5296 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5297 : IdentFlag(0));
5298 Value *ThreadId = getOrCreateThreadID(Ident);
5299 Constant *NumVariables = Builder.getInt32(NumReductions);
5300 const DataLayout &DL = Module->getDataLayout();
5301 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5302 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5303 Function *ReductionFunc = getFreshReductionFunc(*Module);
5304 Value *Lock = getOMPCriticalRegionLock(".reduction");
5306 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5307 : RuntimeFunction::OMPRTL___kmpc_reduce);
5308 CallInst *ReduceCall =
5309 createRuntimeFunctionCall(ReduceFunc,
5310 {Ident, ThreadId, NumVariables, RedArraySize,
5311 RedArray, ReductionFunc, Lock},
5312 "reduce");
5313
5314 // Create final reduction entry blocks for the atomic and non-atomic case.
5315 // Emit IR that dispatches control flow to one of the blocks based on the
5316 // reduction supporting the atomic mode.
5317 BasicBlock *NonAtomicRedBlock =
5318 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5319 BasicBlock *AtomicRedBlock =
5320 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5321 SwitchInst *Switch =
5322 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5323 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5324 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5325
5326 // Populate the non-atomic reduction using the elementwise reduction function.
5327 // This loads the elements from the global and private variables and reduces
5328 // them before storing back the result to the global variable.
5329 Builder.SetInsertPoint(NonAtomicRedBlock);
5330 for (auto En : enumerate(ReductionInfos)) {
5331 const ReductionInfo &RI = En.value();
5333 // We have one less load for by-ref case because that load is now inside of
5334 // the reduction region
5335 Value *RedValue = RI.Variable;
5336 if (!IsByRef[En.index()]) {
5337 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5338 "red.value." + Twine(En.index()));
5339 }
5340 Value *PrivateRedValue =
5341 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5342 "red.private.value." + Twine(En.index()));
5343 Value *Reduced;
5344 InsertPointOrErrorTy AfterIP =
5345 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5346 if (!AfterIP)
5347 return AfterIP.takeError();
5348 Builder.restoreIP(*AfterIP);
5349
5350 if (!Builder.GetInsertBlock())
5351 return InsertPointTy();
5352 // for by-ref case, the load is inside of the reduction region
5353 if (!IsByRef[En.index()])
5354 Builder.CreateStore(Reduced, RI.Variable);
5355 }
5356 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5357 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5358 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5359 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5360 Builder.CreateBr(ContinuationBlock);
5361
5362 // Populate the atomic reduction using the atomic elementwise reduction
5363 // function. There are no loads/stores here because they will be happening
5364 // inside the atomic elementwise reduction.
5365 Builder.SetInsertPoint(AtomicRedBlock);
5366 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5367 for (const ReductionInfo &RI : ReductionInfos) {
5369 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5370 if (!AfterIP)
5371 return AfterIP.takeError();
5372 Builder.restoreIP(*AfterIP);
5373 if (!Builder.GetInsertBlock())
5374 return InsertPointTy();
5375 }
5376 Builder.CreateBr(ContinuationBlock);
5377 } else {
5378 Builder.CreateUnreachable();
5379 }
5380
5381 // Populate the outlined reduction function using the elementwise reduction
5382 // function. Partial values are extracted from the type-erased array of
5383 // pointers to private variables.
5384 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5385 IsByRef, /*isGPU=*/false);
5386 if (Err)
5387 return Err;
5388
5389 if (!Builder.GetInsertBlock())
5390 return InsertPointTy();
5391
5392 Builder.SetInsertPoint(ContinuationBlock);
5393 return Builder.saveIP();
5394}
5395
5398 BodyGenCallbackTy BodyGenCB,
5399 FinalizeCallbackTy FiniCB) {
5400 if (!updateToLocation(Loc))
5401 return Loc.IP;
5402
5403 Directive OMPD = Directive::OMPD_master;
5404 uint32_t SrcLocStrSize;
5405 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5406 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5407 Value *ThreadId = getOrCreateThreadID(Ident);
5408 Value *Args[] = {Ident, ThreadId};
5409
5410 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5411 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5412
5413 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5414 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5415
5416 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5417 /*Conditional*/ true, /*hasFinalize*/ true);
5418}
5419
5422 BodyGenCallbackTy BodyGenCB,
5423 FinalizeCallbackTy FiniCB, Value *Filter) {
5425 if (!updateToLocation(Loc))
5426 return Loc.IP;
5427
5428 Directive OMPD = Directive::OMPD_masked;
5429 uint32_t SrcLocStrSize;
5430 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5431 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5432 Value *ThreadId = getOrCreateThreadID(Ident);
5433 Value *Args[] = {Ident, ThreadId, Filter};
5434 Value *ArgsEnd[] = {Ident, ThreadId};
5435
5436 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5437 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5438
5439 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5440 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5441
5442 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5443 /*Conditional*/ true, /*hasFinalize*/ true);
5444}
5445
5447 llvm::FunctionCallee Callee,
5449 const llvm::Twine &Name) {
5450 llvm::CallInst *Call = Builder.CreateCall(
5451 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5452 Call->setDoesNotThrow();
5453 return Call;
5454}
5455
5456// Expects input basic block is dominated by BeforeScanBB.
5457// Once Scan directive is encountered, the code after scan directive should be
5458// dominated by AfterScanBB. Scan directive splits the code sequence to
5459// scan and input phase. Based on whether inclusive or exclusive
5460// clause is used in the scan directive and whether input loop or scan loop
5461// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5462// input loop and second is the scan loop. The code generated handles only
5463// inclusive scans now.
5465 const LocationDescription &Loc, InsertPointTy AllocaIP,
5466 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5467 bool IsInclusive, ScanInfo *ScanRedInfo) {
5468 if (ScanRedInfo->OMPFirstScanLoop) {
5469 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5470 ScanVarsType, ScanRedInfo);
5471 if (Err)
5472 return Err;
5473 }
5474 if (!updateToLocation(Loc))
5475 return Loc.IP;
5476
5477 llvm::Value *IV = ScanRedInfo->IV;
5478
5479 if (ScanRedInfo->OMPFirstScanLoop) {
5480 // Emit buffer[i] = red; at the end of the input phase.
5481 for (size_t i = 0; i < ScanVars.size(); i++) {
5482 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5483 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5484 Type *DestTy = ScanVarsType[i];
5485 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5486 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5487
5488 Builder.CreateStore(Src, Val);
5489 }
5490 }
5491 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5492 emitBlock(ScanRedInfo->OMPScanDispatch,
5493 Builder.GetInsertBlock()->getParent());
5494
5495 if (!ScanRedInfo->OMPFirstScanLoop) {
5496 IV = ScanRedInfo->IV;
5497 // Emit red = buffer[i]; at the entrance to the scan phase.
5498 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5499 for (size_t i = 0; i < ScanVars.size(); i++) {
5500 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5501 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5502 Type *DestTy = ScanVarsType[i];
5503 Value *SrcPtr =
5504 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5505 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5506 Builder.CreateStore(Src, ScanVars[i]);
5507 }
5508 }
5509
5510 // TODO: Update it to CreateBr and remove dead blocks
5511 llvm::Value *CmpI = Builder.getInt1(true);
5512 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5513 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5514 ScanRedInfo->OMPAfterScanBlock);
5515 } else {
5516 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5517 ScanRedInfo->OMPBeforeScanBlock);
5518 }
5519 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5520 Builder.GetInsertBlock()->getParent());
5521 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5522 return Builder.saveIP();
5523}
5524
5525Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5526 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5527 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5528
5529 Builder.restoreIP(AllocaIP);
5530 // Create the shared pointer at alloca IP.
5531 for (size_t i = 0; i < ScanVars.size(); i++) {
5532 llvm::Value *BuffPtr =
5533 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5534 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5535 }
5536
5537 // Allocate temporary buffer by master thread
5538 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5539 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5540 Builder.restoreIP(CodeGenIP);
5541 Value *AllocSpan =
5542 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5543 for (size_t i = 0; i < ScanVars.size(); i++) {
5544 Type *IntPtrTy = Builder.getInt32Ty();
5545 Constant *Allocsize = ConstantExpr::getSizeOf(ScanVarsType[i]);
5546 Allocsize = ConstantExpr::getTruncOrBitCast(Allocsize, IntPtrTy);
5547 Value *Buff = Builder.CreateMalloc(IntPtrTy, ScanVarsType[i], Allocsize,
5548 AllocSpan, nullptr, "arr");
5549 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5550 }
5551 return Error::success();
5552 };
5553 // TODO: Perform finalization actions for variables. This has to be
5554 // called for variables which have destructors/finalizers.
5555 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5556
5557 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5558 llvm::Value *FilterVal = Builder.getInt32(0);
5560 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5561
5562 if (!AfterIP)
5563 return AfterIP.takeError();
5564 Builder.restoreIP(*AfterIP);
5565 BasicBlock *InputBB = Builder.GetInsertBlock();
5566 if (InputBB->hasTerminator())
5567 Builder.SetInsertPoint(InputBB->getTerminator());
5568 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5569 if (!AfterIP)
5570 return AfterIP.takeError();
5571 Builder.restoreIP(*AfterIP);
5572
5573 return Error::success();
5574}
5575
5576Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5577 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5578 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5579 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5580 Builder.restoreIP(CodeGenIP);
5581 for (ReductionInfo RedInfo : ReductionInfos) {
5582 Value *PrivateVar = RedInfo.PrivateVariable;
5583 Value *OrigVar = RedInfo.Variable;
5584 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5585 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5586
5587 Type *SrcTy = RedInfo.ElementType;
5588 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5589 "arrayOffset");
5590 Value *Src = Builder.CreateLoad(SrcTy, Val);
5591
5592 Builder.CreateStore(Src, OrigVar);
5593 Builder.CreateFree(Buff);
5594 }
5595 return Error::success();
5596 };
5597 // TODO: Perform finalization actions for variables. This has to be
5598 // called for variables which have destructors/finalizers.
5599 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5600
5601 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5602 Builder.SetInsertPoint(TI);
5603 else
5604 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5605
5606 llvm::Value *FilterVal = Builder.getInt32(0);
5608 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5609
5610 if (!AfterIP)
5611 return AfterIP.takeError();
5612 Builder.restoreIP(*AfterIP);
5613 BasicBlock *InputBB = Builder.GetInsertBlock();
5614 if (InputBB->hasTerminator())
5615 Builder.SetInsertPoint(InputBB->getTerminator());
5616 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5617 if (!AfterIP)
5618 return AfterIP.takeError();
5619 Builder.restoreIP(*AfterIP);
5620 return Error::success();
5621}
5622
5624 const LocationDescription &Loc,
5626 ScanInfo *ScanRedInfo) {
5627
5628 if (!updateToLocation(Loc))
5629 return Loc.IP;
5630 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5631 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5632 Builder.restoreIP(CodeGenIP);
5633 Function *CurFn = Builder.GetInsertBlock()->getParent();
5634 // for (int k = 0; k <= ceil(log2(n)); ++k)
5635 llvm::BasicBlock *LoopBB =
5636 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5637 llvm::BasicBlock *ExitBB =
5638 splitBB(Builder, false, "omp.outer.log.scan.exit");
5640 Builder.GetInsertBlock()->getModule(),
5641 (llvm::Intrinsic::ID)llvm::Intrinsic::log2, Builder.getDoubleTy());
5642 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5643 llvm::Value *Arg =
5644 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5645 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5647 Builder.GetInsertBlock()->getModule(),
5648 (llvm::Intrinsic::ID)llvm::Intrinsic::ceil, Builder.getDoubleTy());
5649 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5650 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5651 llvm::Value *NMin1 = Builder.CreateNUWSub(
5652 ScanRedInfo->Span,
5653 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5654 Builder.SetInsertPoint(InputBB);
5655 Builder.CreateBr(LoopBB);
5656 emitBlock(LoopBB, CurFn);
5657 Builder.SetInsertPoint(LoopBB);
5658
5659 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5660 // size pow2k = 1;
5661 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5662 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5663 InputBB);
5664 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5665 InputBB);
5666 // for (size i = n - 1; i >= 2 ^ k; --i)
5667 // tmp[i] op= tmp[i-pow2k];
5668 llvm::BasicBlock *InnerLoopBB =
5669 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5670 llvm::BasicBlock *InnerExitBB =
5671 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5672 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5673 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5674 emitBlock(InnerLoopBB, CurFn);
5675 Builder.SetInsertPoint(InnerLoopBB);
5676 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5677 IVal->addIncoming(NMin1, LoopBB);
5678 for (ReductionInfo RedInfo : ReductionInfos) {
5679 Value *ReductionVal = RedInfo.PrivateVariable;
5680 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5681 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5682 Type *DestTy = RedInfo.ElementType;
5683 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5684 Value *LHSPtr =
5685 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5686 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5687 Value *RHSPtr =
5688 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5689 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5690 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5691 llvm::Value *Result;
5692 InsertPointOrErrorTy AfterIP =
5693 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5694 if (!AfterIP)
5695 return AfterIP.takeError();
5696 Builder.CreateStore(Result, LHSPtr);
5697 }
5698 llvm::Value *NextIVal = Builder.CreateNUWSub(
5699 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5700 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5701 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5702 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5703 emitBlock(InnerExitBB, CurFn);
5704 llvm::Value *Next = Builder.CreateNUWAdd(
5705 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5706 Counter->addIncoming(Next, Builder.GetInsertBlock());
5707 // pow2k <<= 1;
5708 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5709 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5710 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5711 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5712 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5713 return Error::success();
5714 };
5715
5716 // TODO: Perform finalization actions for variables. This has to be
5717 // called for variables which have destructors/finalizers.
5718 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5719
5720 llvm::Value *FilterVal = Builder.getInt32(0);
5722 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5723
5724 if (!AfterIP)
5725 return AfterIP.takeError();
5726 Builder.restoreIP(*AfterIP);
5727 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5728
5729 if (!AfterIP)
5730 return AfterIP.takeError();
5731 Builder.restoreIP(*AfterIP);
5732 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5733 if (Err)
5734 return Err;
5735
5736 return AfterIP;
5737}
5738
5739Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5740 llvm::function_ref<Error()> InputLoopGen,
5741 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5742 ScanInfo *ScanRedInfo) {
5743
5744 {
5745 // Emit loop with input phase:
5746 // for (i: 0..<num_iters>) {
5747 // <input phase>;
5748 // buffer[i] = red;
5749 // }
5750 ScanRedInfo->OMPFirstScanLoop = true;
5751 Error Err = InputLoopGen();
5752 if (Err)
5753 return Err;
5754 }
5755 {
5756 // Emit loop with scan phase:
5757 // for (i: 0..<num_iters>) {
5758 // red = buffer[i];
5759 // <scan phase>;
5760 // }
5761 ScanRedInfo->OMPFirstScanLoop = false;
5762 Error Err = ScanLoopGen(Builder);
5763 if (Err)
5764 return Err;
5765 }
5766 return Error::success();
5767}
5768
5769void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5770 Function *Fun = Builder.GetInsertBlock()->getParent();
5771 ScanRedInfo->OMPScanDispatch =
5772 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5773 ScanRedInfo->OMPAfterScanBlock =
5774 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5775 ScanRedInfo->OMPBeforeScanBlock =
5776 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5777 ScanRedInfo->OMPScanLoopExit =
5778 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5779}
5781 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5782 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5783 Module *M = F->getParent();
5784 LLVMContext &Ctx = M->getContext();
5785 Type *IndVarTy = TripCount->getType();
5786
5787 // Create the basic block structure.
5788 BasicBlock *Preheader =
5789 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5790 BasicBlock *Header =
5791 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5792 BasicBlock *Cond =
5793 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5794 BasicBlock *Body =
5795 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5796 BasicBlock *Latch =
5797 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5798 BasicBlock *Exit =
5799 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5800 BasicBlock *After =
5801 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5802
5803 // Use specified DebugLoc for new instructions.
5804 Builder.SetCurrentDebugLocation(DL);
5805
5806 Builder.SetInsertPoint(Preheader);
5807 Builder.CreateBr(Header);
5808
5809 Builder.SetInsertPoint(Header);
5810 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5811 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5812 Builder.CreateBr(Cond);
5813
5814 Builder.SetInsertPoint(Cond);
5815 Value *Cmp =
5816 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5817 Builder.CreateCondBr(Cmp, Body, Exit);
5818
5819 Builder.SetInsertPoint(Body);
5820 Builder.CreateBr(Latch);
5821
5822 Builder.SetInsertPoint(Latch);
5823 // Decide whether the induction variable increment can carry nsw.
5824 //
5825 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5826 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5827 // for valid programs 0 <= count <= INT_MAX always holds.
5828 //
5829 // Collapsed loops: the trip count is a product that can overflow i32 even for
5830 // a conforming program, so nsw is kept only when the product is a constant
5831 // that provably fits, dropped otherwise.
5832 bool HasNSW = Config.hasNoSignedWrap();
5833 if (HasNSW) {
5834 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5835 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5837 if (CI->getValue().ugt(SignedMax))
5838 HasNSW = false;
5839 } else if (IsCollapsed) {
5840 HasNSW = false;
5841 }
5842 }
5843 Value *Next =
5844 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5845 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5846 Builder.CreateBr(Header);
5847 IndVarPHI->addIncoming(Next, Latch);
5848
5849 Builder.SetInsertPoint(Exit);
5850 Builder.CreateBr(After);
5851
5852 // Remember and return the canonical control flow.
5853 LoopInfos.emplace_front();
5854 CanonicalLoopInfo *CL = &LoopInfos.front();
5855
5856 CL->Header = Header;
5857 CL->Cond = Cond;
5858 CL->Latch = Latch;
5859 CL->Exit = Exit;
5860
5861#ifndef NDEBUG
5862 CL->assertOK();
5863#endif
5864 return CL;
5865}
5866
5869 LoopBodyGenCallbackTy BodyGenCB,
5870 Value *TripCount, const Twine &Name) {
5871 BasicBlock *BB = Loc.IP.getBlock();
5872 BasicBlock *NextBB = BB->getNextNode();
5873
5874 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5875 NextBB, NextBB, Name);
5876 BasicBlock *After = CL->getAfter();
5877
5878 // If location is not set, don't connect the loop.
5879 if (updateToLocation(Loc)) {
5880 // Split the loop at the insertion point: Branch to the preheader and move
5881 // every following instruction to after the loop (the After BB). Also, the
5882 // new successor is the loop's after block.
5883 spliceBB(Builder, After, /*CreateBranch=*/false);
5884 Builder.CreateBr(CL->getPreheader());
5885 }
5886
5887 // Emit the body content. We do it after connecting the loop to the CFG to
5888 // avoid that the callback encounters degenerate BBs.
5889 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5890 return Err;
5891
5892#ifndef NDEBUG
5893 CL->assertOK();
5894#endif
5895 return CL;
5896}
5897
5899 ScanInfos.emplace_front();
5900 ScanInfo *Result = &ScanInfos.front();
5901 return Result;
5902}
5903
5907 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5908 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5909 LocationDescription ComputeLoc =
5910 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5911 updateToLocation(ComputeLoc);
5912
5914
5916 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5917 ScanRedInfo->Span = TripCount;
5918 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5919 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5920
5921 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5922 Builder.restoreIP(CodeGenIP);
5923 ScanRedInfo->IV = IV;
5924 createScanBBs(ScanRedInfo);
5925 BasicBlock *InputBlock = Builder.GetInsertBlock();
5926 Instruction *Terminator = InputBlock->getTerminator();
5927 assert(Terminator->getNumSuccessors() == 1);
5928 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5929 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5930 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5931 Builder.GetInsertBlock()->getParent());
5932 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5933 emitBlock(ScanRedInfo->OMPScanLoopExit,
5934 Builder.GetInsertBlock()->getParent());
5935 Builder.CreateBr(ContinueBlock);
5936 Builder.SetInsertPoint(
5937 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5938 return BodyGenCB(Builder.saveIP(), IV);
5939 };
5940
5941 const auto &&InputLoopGen = [&]() -> Error {
5943 createCanonicalLoop(Builder, BodyGen, Start, Stop, Step, IsSigned,
5944 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5945 if (!LoopInfo)
5946 return LoopInfo.takeError();
5947 Result.push_back(*LoopInfo);
5948 Builder.restoreIP((*LoopInfo)->getAfterIP());
5949 return Error::success();
5950 };
5951 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5953 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5954 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5955 if (!LoopInfo)
5956 return LoopInfo.takeError();
5957 Result.push_back(*LoopInfo);
5958 Builder.restoreIP((*LoopInfo)->getAfterIP());
5959 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5960 return Error::success();
5961 };
5962 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5963 if (Err)
5964 return Err;
5965 return Result;
5966}
5967
5969 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5970 bool IsSigned, bool InclusiveStop, const Twine &Name) {
5971
5972 // Consider the following difficulties (assuming 8-bit signed integers):
5973 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
5974 // DO I = 1, 100, 50
5975 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
5976 // DO I = 100, 0, -128
5977
5978 // Start, Stop and Step must be of the same integer type.
5979 auto *IndVarTy = cast<IntegerType>(Start->getType());
5980 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
5981 assert(IndVarTy == Step->getType() && "Step type mismatch");
5982
5984
5985 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
5986 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
5987
5988 // Like Step, but always positive.
5989 Value *Incr = Step;
5990
5991 // Distance between Start and Stop; always positive.
5992 Value *Span;
5993
5994 // Condition whether there are no iterations are executed at all, e.g. because
5995 // UB < LB.
5996 Value *ZeroCmp;
5997
5998 if (IsSigned) {
5999 // Ensure that increment is positive. If not, negate and invert LB and UB.
6000 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
6001 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
6002 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6003 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6004 Span = Builder.CreateSub(UB, LB, "", false, true);
6005 ZeroCmp = Builder.CreateICmp(
6006 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6007 } else {
6008 Span = Builder.CreateSub(Stop, Start, "", true);
6009 ZeroCmp = Builder.CreateICmp(
6010 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6011 }
6012
6013 Value *CountIfLooping;
6014 if (InclusiveStop) {
6015 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6016 } else {
6017 // Avoid incrementing past stop since it could overflow.
6018 Value *CountIfTwo = Builder.CreateAdd(
6019 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6020 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6021 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6022 }
6023
6024 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6025 "omp_" + Name + ".tripcount");
6026}
6027
6030 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6031 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6032 ScanInfo *ScanRedInfo) {
6033 LocationDescription ComputeLoc =
6034 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6035
6037 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6038
6039 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6040 Builder.restoreIP(CodeGenIP);
6041 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6042 /*HasNSW=*/Config.hasNoSignedWrap());
6043 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6044 /*HasNSW=*/Config.hasNoSignedWrap());
6045 if (InScan)
6046 ScanRedInfo->IV = IndVar;
6047 return BodyGenCB(Builder.saveIP(), IndVar);
6048 };
6049 LocationDescription LoopLoc =
6050 ComputeIP.isSet()
6051 ? Loc
6052 : LocationDescription(Builder.saveIP(),
6053 Builder.getCurrentDebugLocation());
6054 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6055}
6056
6057// Returns an LLVM function to call for initializing loop bounds using OpenMP
6058// static scheduling for composite `distribute parallel for` depending on
6059// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6060// integers as unsigned similarly to CanonicalLoopInfo.
6061static FunctionCallee
6063 OpenMPIRBuilder &OMPBuilder) {
6064 unsigned Bitwidth = Ty->getIntegerBitWidth();
6065 if (Bitwidth == 32)
6066 return OMPBuilder.getOrCreateRuntimeFunction(
6067 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6068 if (Bitwidth == 64)
6069 return OMPBuilder.getOrCreateRuntimeFunction(
6070 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6071 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6072}
6073
6074// Returns an LLVM function to call for initializing loop bounds using OpenMP
6075// static scheduling depending on `type`. Only i32 and i64 are supported by the
6076// runtime. Always interpret integers as unsigned similarly to
6077// CanonicalLoopInfo.
6079 OpenMPIRBuilder &OMPBuilder) {
6080 unsigned Bitwidth = Ty->getIntegerBitWidth();
6081 if (Bitwidth == 32)
6082 return OMPBuilder.getOrCreateRuntimeFunction(
6083 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6084 if (Bitwidth == 64)
6085 return OMPBuilder.getOrCreateRuntimeFunction(
6086 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6087 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6088}
6089
6090OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6091 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6092 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6093 OMPScheduleType DistScheduleSchedType) {
6094 assert(CLI->isValid() && "Requires a valid canonical loop");
6095 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6096 "Require dedicated allocate IP");
6097
6098 // Set up the source location value for OpenMP runtime.
6099 Builder.restoreIP(CLI->getPreheaderIP());
6100 Builder.SetCurrentDebugLocation(DL);
6101
6102 uint32_t SrcLocStrSize;
6103 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6105 switch (LoopType) {
6106 case WorksharingLoopType::ForStaticLoop:
6107 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6108 break;
6109 case WorksharingLoopType::DistributeStaticLoop:
6110 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6111 break;
6112 case WorksharingLoopType::DistributeForStaticLoop:
6113 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6114 break;
6115 }
6116 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6117
6118 // Declare useful OpenMP runtime functions.
6119 Value *IV = CLI->getIndVar();
6120 Type *IVTy = IV->getType();
6121 FunctionCallee StaticInit =
6122 LoopType == WorksharingLoopType::DistributeForStaticLoop
6123 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6124 : getKmpcForStaticInitForType(IVTy, M, *this);
6125 FunctionCallee StaticFini =
6126 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6127
6128 // Allocate space for computed loop bounds as expected by the "init" function.
6129 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6130
6131 Type *I32Type = Type::getInt32Ty(M.getContext());
6132 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6133 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6134 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6135 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6136 CLI->setLastIter(PLastIter);
6137
6138 // At the end of the preheader, prepare for calling the "init" function by
6139 // storing the current loop bounds into the allocated space. A canonical loop
6140 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6141 // and produces an inclusive upper bound.
6142 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6143 Constant *Zero = ConstantInt::get(IVTy, 0);
6144 Constant *One = ConstantInt::get(IVTy, 1);
6145 Builder.CreateStore(Zero, PLowerBound);
6146 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6147 Builder.CreateStore(UpperBound, PUpperBound);
6148 Builder.CreateStore(One, PStride);
6149
6150 Value *ThreadNum =
6151 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6152
6153 OMPScheduleType SchedType =
6154 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6155 ? OMPScheduleType::OrderedDistribute
6157 Constant *SchedulingType =
6158 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6159
6160 // Call the "init" function and update the trip count of the loop with the
6161 // value it produced.
6162 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6163 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6164 this](Value *SchedulingType, auto &Builder) {
6165 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6166 PLowerBound, PUpperBound});
6167 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6168 Value *PDistUpperBound =
6169 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6170 Args.push_back(PDistUpperBound);
6171 }
6172 Args.append({PStride, One, Zero});
6173 createRuntimeFunctionCall(StaticInit, Args);
6174 };
6175 BuildInitCall(SchedulingType, Builder);
6176 if (HasDistSchedule &&
6177 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6178 Constant *DistScheduleSchedType = ConstantInt::get(
6179 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6180 // We want to emit a second init function call for the dist_schedule clause
6181 // to the Distribute construct. This should only be done however if a
6182 // Workshare Loop is nested within a Distribute Construct
6183 BuildInitCall(DistScheduleSchedType, Builder);
6184 }
6185 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6186 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6187 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6188 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6189 CLI->setTripCount(TripCount);
6190
6191 // Update all uses of the induction variable except the one in the condition
6192 // block that compares it with the actual upper bound, and the increment in
6193 // the latch block.
6194
6195 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6196 Builder.SetInsertPoint(CLI->getBody(),
6197 CLI->getBody()->getFirstInsertionPt());
6198 Builder.SetCurrentDebugLocation(DL);
6199 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6200 /*HasNSW=*/Config.hasNoSignedWrap());
6201 });
6202
6203 // In the "exit" block, call the "fini" function.
6204 Builder.SetInsertPoint(CLI->getExit(),
6205 CLI->getExit()->getTerminator()->getIterator());
6206 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6207
6208 // Add the barrier if requested.
6209 if (NeedsBarrier) {
6210 InsertPointOrErrorTy BarrierIP =
6212 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6213 /* CheckCancelFlag */ false);
6214 if (!BarrierIP)
6215 return BarrierIP.takeError();
6216 }
6217
6218 InsertPointTy AfterIP = CLI->getAfterIP();
6219 CLI->invalidate();
6220
6221 return AfterIP;
6222}
6223
6224static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6225 LoopInfo &LI);
6226static void addLoopMetadata(CanonicalLoopInfo *Loop,
6228
6230 LLVMContext &Ctx, Loop *Loop,
6232 SmallVector<Metadata *> &LoopMDList) {
6233 SmallSet<BasicBlock *, 8> Reachable;
6234
6235 // Get the basic blocks from the loop in which memref instructions
6236 // can be found.
6237 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6238 // preferably without running any passes.
6239 for (BasicBlock *Block : Loop->getBlocks()) {
6240 if (Block == CLI->getCond() || Block == CLI->getHeader())
6241 continue;
6242 Reachable.insert(Block);
6243 }
6244
6245 // Add access group metadata to memory-access instructions.
6247 for (BasicBlock *BB : Reachable)
6249 // TODO: If the loop has existing parallel access metadata, have
6250 // to combine two lists.
6251 LoopMDList.push_back(MDNode::get(
6252 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6253}
6254
6256OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6257 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6258 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6259 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6260 assert(CLI->isValid() && "Requires a valid canonical loop");
6261 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6262
6263 LLVMContext &Ctx = CLI->getFunction()->getContext();
6264 Value *IV = CLI->getIndVar();
6265 Value *OrigTripCount = CLI->getTripCount();
6266 Type *IVTy = IV->getType();
6267 assert(IVTy->getIntegerBitWidth() <= 64 &&
6268 "Max supported tripcount bitwidth is 64 bits");
6269 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6270 : Type::getInt64Ty(Ctx);
6271 Type *I32Type = Type::getInt32Ty(M.getContext());
6272 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6273 Constant *One = ConstantInt::get(InternalIVTy, 1);
6274
6275 Function *F = CLI->getFunction();
6276 // Blocks must have terminators.
6277 // FIXME: Don't run analyses on incomplete/invalid IR.
6278 SmallVector<Instruction *> UIs;
6279 for (BasicBlock &BB : *F)
6280 if (!BB.hasTerminator())
6281 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6283 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6284 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6285 LoopAnalysis LIA;
6286 LoopInfo &&LI = LIA.run(*F, FAM);
6287 for (Instruction *I : UIs)
6288 I->eraseFromParent();
6289 Loop *L = LI.getLoopFor(CLI->getHeader());
6290 SmallVector<Metadata *> LoopMDList;
6291 if (ChunkSize || DistScheduleChunkSize)
6292 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6293 addLoopMetadata(CLI, LoopMDList);
6294
6295 // Declare useful OpenMP runtime functions.
6296 FunctionCallee StaticInit =
6297 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6298 FunctionCallee StaticFini =
6299 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6300
6301 // Allocate space for computed loop bounds as expected by the "init" function.
6302 Builder.restoreIP(AllocaIP);
6303 Builder.SetCurrentDebugLocation(DL);
6304 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6305 Value *PLowerBound =
6306 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6307 Value *PUpperBound =
6308 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6309 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6310 CLI->setLastIter(PLastIter);
6311
6312 // Set up the source location value for the OpenMP runtime.
6313 Builder.restoreIP(CLI->getPreheaderIP());
6314 Builder.SetCurrentDebugLocation(DL);
6315
6316 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6317 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6318 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6319 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6320 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6321 "distschedulechunksize");
6322 Value *CastedTripCount =
6323 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6324
6325 Constant *SchedulingType =
6326 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6327 Constant *DistSchedulingType =
6328 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6329 Builder.CreateStore(Zero, PLowerBound);
6330 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6331 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6332 Value *UpperBound =
6333 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6334 Builder.CreateStore(UpperBound, PUpperBound);
6335 Builder.CreateStore(One, PStride);
6336
6337 // Call the "init" function and update the trip count of the loop with the
6338 // value it produced.
6339 uint32_t SrcLocStrSize;
6340 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6341 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6342 if (DistScheduleSchedType != OMPScheduleType::None) {
6343 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6344 }
6345 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6346 Value *ThreadNum =
6347 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6348 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6349 PUpperBound, PStride, One,
6350 this](Value *SchedulingType, Value *ChunkSize,
6351 auto &Builder) {
6353 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6354 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6355 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6356 /*pstride=*/PStride, /*incr=*/One,
6357 /*chunk=*/ChunkSize});
6358 };
6359 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6360 if (DistScheduleSchedType != OMPScheduleType::None &&
6361 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6362 SchedType != OMPScheduleType::OrderedDistribute) {
6363 // We want to emit a second init function call for the dist_schedule clause
6364 // to the Distribute construct. This should only be done however if a
6365 // Workshare Loop is nested within a Distribute Construct
6366 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6367 }
6368
6369 // Load values written by the "init" function.
6370 Value *FirstChunkStart =
6371 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6372 Value *FirstChunkStop =
6373 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6374 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6375 Value *ChunkRange =
6376 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6377 Value *NextChunkStride =
6378 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6379
6380 // Create outer "dispatch" loop for enumerating the chunks.
6381 BasicBlock *DispatchEnter = splitBB(Builder, true);
6382 Value *DispatchCounter;
6383
6384 // It is safe to assume this didn't return an error because the callback
6385 // passed into createCanonicalLoop is the only possible error source, and it
6386 // always returns success.
6387 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6388 {Builder.saveIP(), DL},
6389 [&](InsertPointTy BodyIP, Value *Counter) {
6390 DispatchCounter = Counter;
6391 return Error::success();
6392 },
6393 FirstChunkStart, CastedTripCount, NextChunkStride,
6394 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6395 "dispatch"));
6396
6397 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6398 // not have to preserve the canonical invariant.
6399 BasicBlock *DispatchBody = DispatchCLI->getBody();
6400 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6401 BasicBlock *DispatchExit = DispatchCLI->getExit();
6402 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6403 DispatchCLI->invalidate();
6404
6405 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6406 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6407 redirectTo(CLI->getExit(), DispatchLatch, DL);
6408 redirectTo(DispatchBody, DispatchEnter, DL);
6409
6410 // Prepare the prolog of the chunk loop.
6411 Builder.restoreIP(CLI->getPreheaderIP());
6412 Builder.SetCurrentDebugLocation(DL);
6413
6414 // Compute the number of iterations of the chunk loop.
6415 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6416 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6417 Value *IsLastChunk =
6418 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6419 Value *CountUntilOrigTripCount =
6420 Builder.CreateSub(CastedTripCount, DispatchCounter);
6421 Value *ChunkTripCount = Builder.CreateSelect(
6422 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6423 Value *BackcastedChunkTC =
6424 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6425 CLI->setTripCount(BackcastedChunkTC);
6426
6427 // Update all uses of the induction variable except the one in the condition
6428 // block that compares it with the actual upper bound, and the increment in
6429 // the latch block.
6430 Value *BackcastedDispatchCounter =
6431 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6432 CLI->mapIndVar([&](Instruction *) -> Value * {
6433 Builder.restoreIP(CLI->getBodyIP());
6434 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6435 });
6436
6437 // In the "exit" block, call the "fini" function.
6438 Builder.SetInsertPoint(DispatchExit, DispatchExit->getFirstInsertionPt());
6439 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6440
6441 // Add the barrier if requested.
6442 if (NeedsBarrier) {
6443 InsertPointOrErrorTy AfterIP =
6444 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6445 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6446 if (!AfterIP)
6447 return AfterIP.takeError();
6448 }
6449
6450#ifndef NDEBUG
6451 // Even though we currently do not support applying additional methods to it,
6452 // the chunk loop should remain a canonical loop.
6453 CLI->assertOK();
6454#endif
6455
6456 return InsertPointTy(DispatchAfter, DispatchAfter->getFirstInsertionPt());
6457}
6458
6459// Returns an LLVM function to call for executing an OpenMP static worksharing
6460// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6461// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6462static FunctionCallee
6464 WorksharingLoopType LoopType) {
6465 unsigned Bitwidth = Ty->getIntegerBitWidth();
6466 Module &M = OMPBuilder->M;
6467 switch (LoopType) {
6468 case WorksharingLoopType::ForStaticLoop:
6469 if (Bitwidth == 32)
6470 return OMPBuilder->getOrCreateRuntimeFunction(
6471 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6472 if (Bitwidth == 64)
6473 return OMPBuilder->getOrCreateRuntimeFunction(
6474 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6475 break;
6476 case WorksharingLoopType::DistributeStaticLoop:
6477 if (Bitwidth == 32)
6478 return OMPBuilder->getOrCreateRuntimeFunction(
6479 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6480 if (Bitwidth == 64)
6481 return OMPBuilder->getOrCreateRuntimeFunction(
6482 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6483 break;
6484 case WorksharingLoopType::DistributeForStaticLoop:
6485 if (Bitwidth == 32)
6486 return OMPBuilder->getOrCreateRuntimeFunction(
6487 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6488 if (Bitwidth == 64)
6489 return OMPBuilder->getOrCreateRuntimeFunction(
6490 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6491 break;
6492 }
6493 if (Bitwidth != 32 && Bitwidth != 64) {
6494 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6495 }
6496 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6497}
6498
6499// Inserts a call to proper OpenMP Device RTL function which handles
6500// loop worksharing.
6502 WorksharingLoopType LoopType,
6503 BasicBlock *InsertBlock, Value *Ident,
6504 Value *LoopBodyArg, Value *TripCount,
6505 Function &LoopBodyFn, bool NoLoop) {
6506 Type *TripCountTy = TripCount->getType();
6507 Module &M = OMPBuilder->M;
6508 IRBuilder<> &Builder = OMPBuilder->Builder;
6509 FunctionCallee RTLFn =
6510 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6511 SmallVector<Value *, 8> RealArgs;
6512 RealArgs.push_back(Ident);
6513 RealArgs.push_back(&LoopBodyFn);
6514 RealArgs.push_back(LoopBodyArg);
6515 RealArgs.push_back(TripCount);
6516 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6517 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6518 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6519 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6520 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6521 return;
6522 }
6523 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6524 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6525 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6526 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6527
6528 RealArgs.push_back(
6529 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6530 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6531 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6532 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6533 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6534 } else {
6535 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6536 }
6537
6538 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6539}
6540
6542 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6543 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6544 WorksharingLoopType LoopType, bool NoLoop) {
6545 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6546 BasicBlock *Preheader = CLI->getPreheader();
6547 Value *TripCount = CLI->getTripCount();
6548
6549 // After loop body outling, the loop body contains only set up
6550 // of loop body argument structure and the call to the outlined
6551 // loop body function. Firstly, we need to move setup of loop body args
6552 // into loop preheader.
6553 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6554 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6555
6556 // The next step is to remove the whole loop. We do not it need anymore.
6557 // That's why make an unconditional branch from loop preheader to loop
6558 // exit block
6559 Builder.restoreIP({Preheader, Preheader->end()});
6560 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6561 Preheader->getTerminator()->eraseFromParent();
6562 Builder.CreateBr(CLI->getExit());
6563
6564 // Delete dead loop blocks
6565 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6566 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6567 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6568 CleanUpInfo.EntryBB = CLI->getHeader();
6569 CleanUpInfo.ExitBB = CLI->getExit();
6570 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6571 DeleteDeadBlocks(BlocksToBeRemoved);
6572
6573 // Find the instruction which corresponds to loop body argument structure
6574 // and remove the call to loop body function instruction.
6575 Value *LoopBodyArg;
6576 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6577 assert(OutlinedFnUser &&
6578 "Expected unique undroppable user of outlined function");
6579 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6580 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6581 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6582 "Expected outlined function call to be located in loop preheader");
6583 // Check in case no argument structure has been passed.
6584 if (OutlinedFnCallInstruction->arg_size() > 1)
6585 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6586 else
6587 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6588 OutlinedFnCallInstruction->eraseFromParent();
6589
6590 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6591 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6592
6593 for (auto &ToBeDeletedItem : ToBeDeleted)
6594 ToBeDeletedItem->eraseFromParent();
6595 CLI->invalidate();
6596}
6597
6598OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6599 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6600 WorksharingLoopType LoopType, bool NoLoop) {
6601 uint32_t SrcLocStrSize;
6602 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6604 switch (LoopType) {
6605 case WorksharingLoopType::ForStaticLoop:
6606 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6607 break;
6608 case WorksharingLoopType::DistributeStaticLoop:
6609 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6610 break;
6611 case WorksharingLoopType::DistributeForStaticLoop:
6612 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6613 break;
6614 }
6615 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6616
6617 auto OI = std::make_unique<OutlineInfo>();
6618 OI->OuterAllocBB = CLI->getPreheader();
6619 Function *OuterFn = CLI->getPreheader()->getParent();
6620
6621 // Instructions which need to be deleted at the end of code generation
6622 SmallVector<Instruction *, 4> ToBeDeleted;
6623
6624 OI->OuterAllocBB = AllocaIP.getBlock();
6625
6626 // Mark the body loop as region which needs to be extracted
6627 OI->EntryBB = CLI->getBody();
6628 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6629 "omp.prelatch");
6630
6631 // Prepare loop body for extraction
6632 Builder.restoreIP({CLI->getPreheader(), CLI->getPreheader()->begin()});
6633
6634 // Insert new loop counter variable which will be used only in loop
6635 // body.
6636 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6637 Instruction *NewLoopCntLoad =
6638 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6639 // New loop counter instructions are redundant in the loop preheader when
6640 // code generation for workshare loop is finshed. That's why mark them as
6641 // ready for deletion.
6642 ToBeDeleted.push_back(NewLoopCntLoad);
6643 ToBeDeleted.push_back(NewLoopCnt);
6644
6645 // Analyse loop body region. Find all input variables which are used inside
6646 // loop body region.
6647 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6649 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6650
6651 CodeExtractorAnalysisCache CEAC(*OuterFn);
6652 CodeExtractor Extractor(Blocks,
6653 /* DominatorTree */ nullptr,
6654 /* AggregateArgs */ true,
6655 /* BlockFrequencyInfo */ nullptr,
6656 /* BranchProbabilityInfo */ nullptr,
6657 /* AssumptionCache */ nullptr,
6658 /* AllowVarArgs */ true,
6659 /* AllowAlloca */ true,
6660 /* AllocationBlock */ CLI->getPreheader(),
6661 /* DeallocationBlocks */ {},
6662 /* Suffix */ ".omp_wsloop",
6663 /* AggrArgsIn0AddrSpace */ true);
6664
6665 BasicBlock *CommonExit = nullptr;
6666 SetVector<Value *> SinkingCands, HoistingCands;
6667
6668 // Find allocas outside the loop body region which are used inside loop
6669 // body
6670 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6671
6672 // We need to model loop body region as the function f(cnt, loop_arg).
6673 // That's why we replace loop induction variable by the new counter
6674 // which will be one of loop body function argument
6676 CLI->getIndVar()->user_end());
6677 for (auto Use : Users) {
6678 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6679 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6680 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6681 }
6682 }
6683 }
6684 // Make sure that loop counter variable is not merged into loop body
6685 // function argument structure and it is passed as separate variable
6686 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6687
6688 // PostOutline CB is invoked when loop body function is outlined and
6689 // loop body is replaced by call to outlined function. We need to add
6690 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6691 // function will handle loop control logic.
6692 //
6693 OI->PostOutlineCB = [=, ToBeDeletedVec =
6694 std::move(ToBeDeleted)](Function &OutlinedFn) {
6695 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6696 LoopType, NoLoop);
6697 };
6698 addOutlineInfo(std::move(OI));
6699 return CLI->getAfterIP();
6700}
6701
6704 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6705 bool HasSimdModifier, bool HasMonotonicModifier,
6706 bool HasNonmonotonicModifier, bool HasOrderedClause,
6707 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6708 Value *DistScheduleChunkSize) {
6709 if (Config.isTargetDevice())
6710 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NoLoop);
6711 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6712 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6713 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6714
6715 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6716 OMPScheduleType::ModifierOrdered;
6717 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6718 if (HasDistSchedule) {
6719 DistScheduleSchedType = DistScheduleChunkSize
6720 ? OMPScheduleType::OrderedDistributeChunked
6721 : OMPScheduleType::OrderedDistribute;
6722 }
6723 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6724 case OMPScheduleType::BaseStatic:
6725 case OMPScheduleType::BaseDistribute:
6726 assert((!ChunkSize || !DistScheduleChunkSize) &&
6727 "No chunk size with static-chunked schedule");
6728 if (IsOrdered && !HasDistSchedule)
6729 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6730 NeedsBarrier, ChunkSize);
6731 // FIXME: Monotonicity ignored?
6732 if (DistScheduleChunkSize)
6733 return applyStaticChunkedWorkshareLoop(
6734 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6735 DistScheduleChunkSize, DistScheduleSchedType);
6736 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6737 HasDistSchedule);
6738
6739 case OMPScheduleType::BaseStaticChunked:
6740 case OMPScheduleType::BaseDistributeChunked:
6741 if (IsOrdered && !HasDistSchedule)
6742 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6743 NeedsBarrier, ChunkSize);
6744 // FIXME: Monotonicity ignored?
6745 return applyStaticChunkedWorkshareLoop(
6746 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6747 DistScheduleChunkSize, DistScheduleSchedType);
6748
6749 case OMPScheduleType::BaseRuntime:
6750 case OMPScheduleType::BaseAuto:
6751 case OMPScheduleType::BaseGreedy:
6752 case OMPScheduleType::BaseBalanced:
6753 case OMPScheduleType::BaseSteal:
6754 case OMPScheduleType::BaseRuntimeSimd:
6755 assert(!ChunkSize &&
6756 "schedule type does not support user-defined chunk sizes");
6757 [[fallthrough]];
6758 case OMPScheduleType::BaseGuidedSimd:
6759 case OMPScheduleType::BaseDynamicChunked:
6760 case OMPScheduleType::BaseGuidedChunked:
6761 case OMPScheduleType::BaseGuidedIterativeChunked:
6762 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6763 case OMPScheduleType::BaseStaticBalancedChunked:
6764 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6765 NeedsBarrier, ChunkSize);
6766
6767 default:
6768 llvm_unreachable("Unknown/unimplemented schedule kind");
6769 }
6770}
6771
6772/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6773/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6774/// the runtime. Always interpret integers as unsigned similarly to
6775/// CanonicalLoopInfo.
6776static FunctionCallee
6778 unsigned Bitwidth = Ty->getIntegerBitWidth();
6779 if (Bitwidth == 32)
6780 return OMPBuilder.getOrCreateRuntimeFunction(
6781 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6782 if (Bitwidth == 64)
6783 return OMPBuilder.getOrCreateRuntimeFunction(
6784 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6785 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6786}
6787
6788/// Returns an LLVM function to call for updating the next loop using OpenMP
6789/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6790/// the runtime. Always interpret integers as unsigned similarly to
6791/// CanonicalLoopInfo.
6792static FunctionCallee
6794 unsigned Bitwidth = Ty->getIntegerBitWidth();
6795 if (Bitwidth == 32)
6796 return OMPBuilder.getOrCreateRuntimeFunction(
6797 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6798 if (Bitwidth == 64)
6799 return OMPBuilder.getOrCreateRuntimeFunction(
6800 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6801 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6802}
6803
6804/// Returns an LLVM function to call for finalizing the dynamic loop using
6805/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6806/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6807static FunctionCallee
6809 unsigned Bitwidth = Ty->getIntegerBitWidth();
6810 if (Bitwidth == 32)
6811 return OMPBuilder.getOrCreateRuntimeFunction(
6812 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6813 if (Bitwidth == 64)
6814 return OMPBuilder.getOrCreateRuntimeFunction(
6815 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6816 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6817}
6818
6820OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6821 InsertPointTy AllocaIP,
6822 OMPScheduleType SchedType,
6823 bool NeedsBarrier, Value *Chunk) {
6824 assert(CLI->isValid() && "Requires a valid canonical loop");
6825 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6826 "Require dedicated allocate IP");
6828 "Require valid schedule type");
6829
6830 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6831 OMPScheduleType::ModifierOrdered;
6832
6833 // Set up the source location value for OpenMP runtime.
6834 Builder.SetCurrentDebugLocation(DL);
6835
6836 uint32_t SrcLocStrSize;
6837 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6838 Value *SrcLoc =
6839 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6840
6841 // Declare useful OpenMP runtime functions.
6842 Value *IV = CLI->getIndVar();
6843 Type *IVTy = IV->getType();
6844 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6845 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6846
6847 // Allocate space for computed loop bounds as expected by the "init" function.
6848 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6849 Type *I32Type = Type::getInt32Ty(M.getContext());
6850 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6851 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6852 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6853 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6854 CLI->setLastIter(PLastIter);
6855
6856 // At the end of the preheader, prepare for calling the "init" function by
6857 // storing the current loop bounds into the allocated space. A canonical loop
6858 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6859 // and produces an inclusive upper bound.
6860 BasicBlock *PreHeader = CLI->getPreheader();
6861 Builder.SetInsertPoint(PreHeader->getTerminator());
6862 Constant *One = ConstantInt::get(IVTy, 1);
6863 Builder.CreateStore(One, PLowerBound);
6864 Value *UpperBound = CLI->getTripCount();
6865 Builder.CreateStore(UpperBound, PUpperBound);
6866 Builder.CreateStore(One, PStride);
6867
6868 BasicBlock *Header = CLI->getHeader();
6869 BasicBlock *Exit = CLI->getExit();
6870 BasicBlock *Cond = CLI->getCond();
6871 BasicBlock *Latch = CLI->getLatch();
6872 InsertPointTy AfterIP = CLI->getAfterIP();
6873
6874 // The CLI will be "broken" in the code below, as the loop is no longer
6875 // a valid canonical loop.
6876
6877 if (!Chunk)
6878 Chunk = One;
6879
6880 Value *ThreadNum =
6881 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6882
6883 Constant *SchedulingType =
6884 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6885
6886 // Call the "init" function.
6887 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6888 /* LowerBound */ One, UpperBound,
6889 /* step */ One, Chunk});
6890
6891 // An outer loop around the existing one.
6892 BasicBlock *OuterCond = BasicBlock::Create(
6893 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6894 PreHeader->getParent());
6895 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6896 Builder.SetInsertPoint(OuterCond, OuterCond->getFirstInsertionPt());
6898 DynamicNext,
6899 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6900 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6901 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6902 Value *LowerBound =
6903 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6904 Builder.CreateCondBr(MoreWork, Header, Exit);
6905
6906 // Change PHI-node in loop header to use outer cond rather than preheader,
6907 // and set IV to the LowerBound.
6908 Instruction *Phi = &Header->front();
6909 auto *PI = cast<PHINode>(Phi);
6910 PI->setIncomingBlock(0, OuterCond);
6911 PI->setIncomingValue(0, LowerBound);
6912
6913 // Then set the pre-header to jump to the OuterCond
6914 Instruction *Term = PreHeader->getTerminator();
6915 auto *Br = cast<UncondBrInst>(Term);
6916 Br->setSuccessor(OuterCond);
6917
6918 // Modify the inner condition:
6919 // * Use the UpperBound returned from the DynamicNext call.
6920 // * jump to the loop outer loop when done with one of the inner loops.
6921 Builder.SetInsertPoint(Cond, Cond->getFirstInsertionPt());
6922 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
6923 Instruction *Comp = &*Builder.GetInsertPoint();
6924 auto *CI = cast<CmpInst>(Comp);
6925 CI->setOperand(1, UpperBound);
6926 // Redirect the inner exit to branch to outer condition.
6927 Instruction *Branch = &Cond->back();
6928 auto *BI = cast<CondBrInst>(Branch);
6929 assert(BI->getSuccessor(1) == Exit);
6930 BI->setSuccessor(1, OuterCond);
6931
6932 // Call the "fini" function if "ordered" is present in wsloop directive.
6933 if (Ordered) {
6934 Builder.SetInsertPoint(&Latch->back());
6935 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
6936 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
6937 }
6938
6939 // Add the barrier if requested.
6940 if (NeedsBarrier) {
6941 Builder.SetInsertPoint(&Exit->back());
6942 InsertPointOrErrorTy BarrierIP =
6944 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6945 /* CheckCancelFlag */ false);
6946 if (!BarrierIP)
6947 return BarrierIP.takeError();
6948 }
6949
6950 CLI->invalidate();
6951 return AfterIP;
6952}
6953
6954/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
6955/// after this \p OldTarget will be orphaned.
6957 BasicBlock *NewTarget, DebugLoc DL) {
6958 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
6959 redirectTo(Pred, NewTarget, DL);
6960}
6961
6963 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
6964 // We add a block to BBsToKeep iff we have proven it has an external use.
6966
6967 while (true) {
6968 bool Changed = false;
6969
6970 for (BasicBlock *BB : BBs) {
6971 if (BBsToKeep.contains(BB))
6972 continue;
6973
6974 for (Use &U : BB->uses()) {
6975 auto *UseInst = dyn_cast<Instruction>(U.getUser());
6976 if (!UseInst)
6977 continue;
6978 BasicBlock *UseBB = UseInst->getParent();
6979 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
6980 BBsToKeep.insert(BB);
6981 Changed = true;
6982 break;
6983 }
6984 }
6985 }
6986
6987 if (!Changed)
6988 break;
6989 }
6990
6992 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
6993 DeleteDeadBlocks(BBsToDelete);
6994}
6995
6996CanonicalLoopInfo *
6998 InsertPointTy ComputeIP) {
6999 assert(Loops.size() >= 1 && "At least one loop required");
7000 size_t NumLoops = Loops.size();
7001
7002 // Nothing to do if there is already just one loop.
7003 if (NumLoops == 1)
7004 return Loops.front();
7005
7006 CanonicalLoopInfo *Outermost = Loops.front();
7007 CanonicalLoopInfo *Innermost = Loops.back();
7008 BasicBlock *OrigPreheader = Outermost->getPreheader();
7009 BasicBlock *OrigAfter = Outermost->getAfter();
7010 Function *F = OrigPreheader->getParent();
7011
7012 // Loop control blocks that may become orphaned later.
7013 SmallVector<BasicBlock *, 12> OldControlBBs;
7014 OldControlBBs.reserve(6 * Loops.size());
7016 Loop->collectControlBlocks(OldControlBBs);
7017
7018 // Setup the IRBuilder for inserting the trip count computation.
7019 Builder.SetCurrentDebugLocation(DL);
7020 if (ComputeIP.isSet())
7021 Builder.restoreIP(ComputeIP);
7022 else
7023 Builder.restoreIP(Outermost->getPreheaderIP());
7024
7025 // Derive the collapsed' loop trip count.
7026 // TODO: Find common/largest indvar type.
7027 Value *CollapsedTripCount = nullptr;
7028 for (CanonicalLoopInfo *L : Loops) {
7029 assert(L->isValid() &&
7030 "All loops to collapse must be valid canonical loops");
7031 Value *OrigTripCount = L->getTripCount();
7032 if (!CollapsedTripCount) {
7033 CollapsedTripCount = OrigTripCount;
7034 continue;
7035 }
7036
7037 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7038 CollapsedTripCount =
7039 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7040 }
7041
7042 // Create the collapsed loop control flow.
7043 CanonicalLoopInfo *Result =
7044 createLoopSkeleton(DL, CollapsedTripCount, F,
7045 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7046 /*IsCollapsed=*/true);
7047
7048 // Build the collapsed loop body code.
7049 // Start with deriving the input loop induction variables from the collapsed
7050 // one, using a divmod scheme. To preserve the original loops' order, the
7051 // innermost loop use the least significant bits.
7052 Builder.restoreIP(Result->getBodyIP());
7053
7054 Value *Leftover = Result->getIndVar();
7055 SmallVector<Value *> NewIndVars;
7056 NewIndVars.resize(NumLoops);
7057 for (int i = NumLoops - 1; i >= 1; --i) {
7058 Value *OrigTripCount = Loops[i]->getTripCount();
7059
7060 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7061 NewIndVars[i] = NewIndVar;
7062
7063 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7064 }
7065 // Outermost loop gets all the remaining bits.
7066 NewIndVars[0] = Leftover;
7067
7068 // Construct the loop body control flow.
7069 // We progressively construct the branch structure following in direction of
7070 // the control flow, from the leading in-between code, the loop nest body, the
7071 // trailing in-between code, and rejoining the collapsed loop's latch.
7072 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7073 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7074 // its predecessors as sources.
7075 BasicBlock *ContinueBlock = Result->getBody();
7076 BasicBlock *ContinuePred = nullptr;
7077 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7078 BasicBlock *NextSrc) {
7079 if (ContinueBlock)
7080 redirectTo(ContinueBlock, Dest, DL);
7081 else
7082 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7083
7084 ContinueBlock = nullptr;
7085 ContinuePred = NextSrc;
7086 };
7087
7088 // The code before the nested loop of each level.
7089 // Because we are sinking it into the nest, it will be executed more often
7090 // that the original loop. More sophisticated schemes could keep track of what
7091 // the in-between code is and instantiate it only once per thread.
7092 for (size_t i = 0; i < NumLoops - 1; ++i)
7093 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7094
7095 // Connect the loop nest body.
7096 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7097
7098 // The code after the nested loop at each level.
7099 for (size_t i = NumLoops - 1; i > 0; --i)
7100 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7101
7102 // Connect the finished loop to the collapsed loop latch.
7103 ContinueWith(Result->getLatch(), nullptr);
7104
7105 // Replace the input loops with the new collapsed loop.
7106 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7107 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7108
7109 // Replace the input loop indvars with the derived ones.
7110 for (size_t i = 0; i < NumLoops; ++i)
7111 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7112
7113 // Remove unused parts of the input loops.
7114 removeUnusedBlocksFromParent(OldControlBBs);
7115
7116 for (CanonicalLoopInfo *L : Loops)
7117 L->invalidate();
7118
7119#ifndef NDEBUG
7120 Result->assertOK();
7121#endif
7122 return Result;
7123}
7124
7125std::vector<CanonicalLoopInfo *>
7127 ArrayRef<Value *> TileSizes) {
7128 assert(TileSizes.size() == Loops.size() &&
7129 "Must pass as many tile sizes as there are loops");
7130 int NumLoops = Loops.size();
7131 assert(NumLoops >= 1 && "At least one loop to tile required");
7132
7133 CanonicalLoopInfo *OutermostLoop = Loops.front();
7134 CanonicalLoopInfo *InnermostLoop = Loops.back();
7135 Function *F = OutermostLoop->getBody()->getParent();
7136 BasicBlock *InnerEnter = InnermostLoop->getBody();
7137 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7138
7139 // Loop control blocks that may become orphaned later.
7140 SmallVector<BasicBlock *, 12> OldControlBBs;
7141 OldControlBBs.reserve(6 * Loops.size());
7143 Loop->collectControlBlocks(OldControlBBs);
7144
7145 // Collect original trip counts and induction variable to be accessible by
7146 // index. Also, the structure of the original loops is not preserved during
7147 // the construction of the tiled loops, so do it before we scavenge the BBs of
7148 // any original CanonicalLoopInfo.
7149 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7150 for (CanonicalLoopInfo *L : Loops) {
7151 assert(L->isValid() && "All input loops must be valid canonical loops");
7152 OrigTripCounts.push_back(L->getTripCount());
7153 OrigIndVars.push_back(L->getIndVar());
7154 }
7155
7156 // Collect the code between loop headers. These may contain SSA definitions
7157 // that are used in the loop nest body. To be usable with in the innermost
7158 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7159 // these instructions may be executed more often than before the tiling.
7160 // TODO: It would be sufficient to only sink them into body of the
7161 // corresponding tile loop.
7163 for (int i = 0; i < NumLoops - 1; ++i) {
7164 CanonicalLoopInfo *Surrounding = Loops[i];
7165 CanonicalLoopInfo *Nested = Loops[i + 1];
7166
7167 BasicBlock *EnterBB = Surrounding->getBody();
7168 BasicBlock *ExitBB = Nested->getHeader();
7169 InbetweenCode.emplace_back(EnterBB, ExitBB);
7170 }
7171
7172 // Compute the trip counts of the floor loops.
7173 Builder.SetCurrentDebugLocation(DL);
7174 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7175 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7176 for (int i = 0; i < NumLoops; ++i) {
7177 Value *TileSize = TileSizes[i];
7178 Value *OrigTripCount = OrigTripCounts[i];
7179 Type *IVType = OrigTripCount->getType();
7180
7181 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7182 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7183
7184 // 0 if tripcount divides the tilesize, 1 otherwise.
7185 // 1 means we need an additional iteration for a partial tile.
7186 //
7187 // Unfortunately we cannot just use the roundup-formula
7188 // (tripcount + tilesize - 1)/tilesize
7189 // because the summation might overflow. We do not want introduce undefined
7190 // behavior when the untiled loop nest did not.
7191 Value *FloorTripOverflow =
7192 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7193
7194 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7195 Value *FloorTripCount =
7196 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7197 "omp_floor" + Twine(i) + ".tripcount", true);
7198
7199 // Remember some values for later use.
7200 FloorCompleteCount.push_back(FloorCompleteTripCount);
7201 FloorCount.push_back(FloorTripCount);
7202 FloorRems.push_back(FloorTripRem);
7203 }
7204
7205 // Generate the new loop nest, from the outermost to the innermost.
7206 std::vector<CanonicalLoopInfo *> Result;
7207 Result.reserve(NumLoops * 2);
7208
7209 // The basic block of the surrounding loop that enters the nest generated
7210 // loop.
7211 BasicBlock *Enter = OutermostLoop->getPreheader();
7212
7213 // The basic block of the surrounding loop where the inner code should
7214 // continue.
7215 BasicBlock *Continue = OutermostLoop->getAfter();
7216
7217 // Where the next loop basic block should be inserted.
7218 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7219
7220 auto EmbeddNewLoop =
7221 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7222 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7223 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7224 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7225 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7226 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7227
7228 // Setup the position where the next embedded loop connects to this loop.
7229 Enter = EmbeddedLoop->getBody();
7230 Continue = EmbeddedLoop->getLatch();
7231 OutroInsertBefore = EmbeddedLoop->getLatch();
7232 return EmbeddedLoop;
7233 };
7234
7235 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7236 const Twine &NameBase) {
7237 for (auto P : enumerate(TripCounts)) {
7238 CanonicalLoopInfo *EmbeddedLoop =
7239 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7240 Result.push_back(EmbeddedLoop);
7241 }
7242 };
7243
7244 EmbeddNewLoops(FloorCount, "floor");
7245
7246 // Within the innermost floor loop, emit the code that computes the tile
7247 // sizes.
7248 Builder.SetInsertPoint(Enter->getTerminator());
7249 SmallVector<Value *, 4> TileCounts;
7250 for (int i = 0; i < NumLoops; ++i) {
7251 CanonicalLoopInfo *FloorLoop = Result[i];
7252 Value *TileSize = TileSizes[i];
7253
7254 Value *FloorIsEpilogue =
7255 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7256 Value *TileTripCount =
7257 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7258
7259 TileCounts.push_back(TileTripCount);
7260 }
7261
7262 // Create the tile loops.
7263 EmbeddNewLoops(TileCounts, "tile");
7264
7265 // Insert the inbetween code into the body.
7266 BasicBlock *BodyEnter = Enter;
7267 BasicBlock *BodyEntered = nullptr;
7268 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7269 BasicBlock *EnterBB = P.first;
7270 BasicBlock *ExitBB = P.second;
7271
7272 if (BodyEnter)
7273 redirectTo(BodyEnter, EnterBB, DL);
7274 else
7275 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7276
7277 BodyEnter = nullptr;
7278 BodyEntered = ExitBB;
7279 }
7280
7281 // Append the original loop nest body into the generated loop nest body.
7282 if (BodyEnter)
7283 redirectTo(BodyEnter, InnerEnter, DL);
7284 else
7285 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7287
7288 // Replace the original induction variable with an induction variable computed
7289 // from the tile and floor induction variables.
7290 Builder.restoreIP(Result.back()->getBodyIP());
7291 for (int i = 0; i < NumLoops; ++i) {
7292 CanonicalLoopInfo *FloorLoop = Result[i];
7293 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7294 Value *OrigIndVar = OrigIndVars[i];
7295 Value *Size = TileSizes[i];
7296
7297 Value *Scale =
7298 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7299 Value *Shift =
7300 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7301 OrigIndVar->replaceAllUsesWith(Shift);
7302 }
7303
7304 // Remove unused parts of the original loops.
7305 removeUnusedBlocksFromParent(OldControlBBs);
7306
7307 for (CanonicalLoopInfo *L : Loops)
7308 L->invalidate();
7309
7310#ifndef NDEBUG
7311 for (CanonicalLoopInfo *GenL : Result)
7312 GenL->assertOK();
7313#endif
7314 return Result;
7315}
7316
7317/// Attach metadata \p Properties to the basic block described by \p BB. If the
7318/// basic block already has metadata, the basic block properties are appended.
7321 // Nothing to do if no property to attach.
7322 if (Properties.empty())
7323 return;
7324
7325 LLVMContext &Ctx = BB->getContext();
7326 SmallVector<Metadata *> NewProperties;
7327 NewProperties.push_back(nullptr);
7328
7329 // If the basic block already has metadata, prepend it to the new metadata.
7330 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7331 if (Existing)
7332 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7333
7334 append_range(NewProperties, Properties);
7335 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7336 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7337
7338 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7339}
7340
7341/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7342/// loop already has metadata, the loop properties are appended.
7345 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7346
7347 // Attach metadata to the loop's latch
7348 BasicBlock *Latch = Loop->getLatch();
7349 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7351}
7352
7353/// Attach llvm.access.group metadata to the memref instructions of \p Block
7355 LoopInfo &LI) {
7356 for (Instruction &I : *Block) {
7357 if (I.mayReadOrWriteMemory()) {
7358 // TODO: This instruction may already have access group from
7359 // other pragmas e.g. #pragma clang loop vectorize. Append
7360 // so that the existing metadata is not overwritten.
7361 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7362 }
7363 }
7364}
7365
7366CanonicalLoopInfo *
7368 CanonicalLoopInfo *firstLoop = Loops.front();
7369 CanonicalLoopInfo *lastLoop = Loops.back();
7370 Function *F = firstLoop->getPreheader()->getParent();
7371
7372 // Loop control blocks that will become orphaned later
7373 SmallVector<BasicBlock *> oldControlBBs;
7375 Loop->collectControlBlocks(oldControlBBs);
7376
7377 // Collect original trip counts
7378 SmallVector<Value *> origTripCounts;
7379 for (CanonicalLoopInfo *L : Loops) {
7380 assert(L->isValid() && "All input loops must be valid canonical loops");
7381 origTripCounts.push_back(L->getTripCount());
7382 }
7383
7384 Builder.SetCurrentDebugLocation(DL);
7385
7386 // Compute max trip count.
7387 // The fused loop will be from 0 to max(origTripCounts)
7388 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7389 F, firstLoop->getHeader());
7390 Builder.SetInsertPoint(TCBlock);
7391 Value *fusedTripCount = nullptr;
7392 for (CanonicalLoopInfo *L : Loops) {
7393 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7394 Value *origTripCount = L->getTripCount();
7395 if (!fusedTripCount) {
7396 fusedTripCount = origTripCount;
7397 continue;
7398 }
7399 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7400 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7401 ".omp.fuse.tc");
7402 }
7403
7404 // Generate new loop
7405 CanonicalLoopInfo *fused =
7406 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7407 lastLoop->getLatch(), "fused");
7408
7409 // Replace original loops with the fused loop
7410 // Preheader and After are not considered inside the CLI.
7411 // These are used to compute the individual TCs of the loops
7412 // so they have to be put before the resulting fused loop.
7413 // Moving them up for readability.
7414 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7415 Loops[i]->getPreheader()->moveBefore(TCBlock);
7416 Loops[i]->getAfter()->moveBefore(TCBlock);
7417 }
7418 lastLoop->getPreheader()->moveBefore(TCBlock);
7419
7420 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7421 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7422 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7423 }
7424 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7425 redirectTo(TCBlock, fused->getPreheader(), DL);
7426 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7427
7428 // Build the fused body
7429 // Create new Blocks with conditions that jump to the original loop bodies
7431 SmallVector<Value *> condValues;
7432 for (size_t i = 0; i < Loops.size(); ++i) {
7433 BasicBlock *condBlock = BasicBlock::Create(
7434 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7435 Builder.SetInsertPoint(condBlock);
7436 Value *condValue =
7437 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7438 condBBs.push_back(condBlock);
7439 condValues.push_back(condValue);
7440 }
7441 // Join the condition blocks with the bodies of the original loops
7442 redirectTo(fused->getBody(), condBBs[0], DL);
7443 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7444 Builder.SetInsertPoint(condBBs[i]);
7445 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7446 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7447 // Replace the IV with the fused IV
7448 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7449 }
7450 // Last body jumps to the created end body block
7451 Builder.SetInsertPoint(condBBs.back());
7452 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7453 fused->getLatch());
7454 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7455 // Replace the IV with the fused IV
7456 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7457
7458 // The loop latch must have only one predecessor. Currently it is branched to
7459 // from both the last condition block and the last loop body
7460 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7461 "omp.fused.pre_latch");
7462
7463 // Remove unused parts
7464 removeUnusedBlocksFromParent(oldControlBBs);
7465
7466 // Invalidate old CLIs
7467 for (CanonicalLoopInfo *L : Loops)
7468 L->invalidate();
7469
7470#ifndef NDEBUG
7471 fused->assertOK();
7472#endif
7473 return fused;
7474}
7475
7477 LLVMContext &Ctx = Builder.getContext();
7479 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7480 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7481}
7482
7484 LLVMContext &Ctx = Builder.getContext();
7486 Loop, {
7487 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7488 });
7489}
7490
7491void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7492 Value *IfCond, ValueToValueMapTy &VMap,
7493 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7494 const Twine &NamePrefix) {
7495 Function *F = CanonicalLoop->getFunction();
7496
7497 // We can't do
7498 // if (cond) {
7499 // simd_loop;
7500 // } else {
7501 // non_simd_loop;
7502 // }
7503 // because then the CanonicalLoopInfo would only point to one of the loops:
7504 // leading to other constructs operating on the same loop to malfunction.
7505 // Instead generate
7506 // while (...) {
7507 // if (cond) {
7508 // simd_body;
7509 // } else {
7510 // not_simd_body;
7511 // }
7512 // }
7513 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7514 // body at -O3
7515
7516 // Define where if branch should be inserted
7517 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7518
7519 // Create additional blocks for the if statement
7520 BasicBlock *Cond = SplitBeforeIt->getParent();
7521 llvm::LLVMContext &C = Cond->getContext();
7523 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7525 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7526
7527 // Create if condition branch.
7528 Builder.SetInsertPoint(SplitBeforeIt);
7529 Instruction *BrInstr =
7530 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7531 InsertPointTy IP{BrInstr->getParent(), ++BrInstr->getIterator()};
7532 // Then block contains branch to omp loop body which needs to be vectorized
7533 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7534 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7535
7536 Builder.SetInsertPoint(ElseBlock);
7537
7538 // Clone loop for the else branch
7540
7541 SmallVector<BasicBlock *, 8> ExistingBlocks;
7542 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7543 ExistingBlocks.push_back(ThenBlock);
7544 ExistingBlocks.append(L->block_begin(), L->block_end());
7545 // Cond is the block that has the if clause condition
7546 // LoopCond is omp_loop.cond
7547 // LoopHeader is omp_loop.header
7548 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7549 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7550 assert(LoopCond && LoopHeader && "Invalid loop structure");
7551 for (BasicBlock *Block : ExistingBlocks) {
7552 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7553 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7554 continue;
7555 }
7556 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7557
7558 // fix name not to be omp.if.then
7559 if (Block == ThenBlock)
7560 NewBB->setName(NamePrefix + ".if.else");
7561
7562 NewBB->moveBefore(CanonicalLoop->getExit());
7563 VMap[Block] = NewBB;
7564 NewBlocks.push_back(NewBB);
7565 }
7566 remapInstructionsInBlocks(NewBlocks, VMap);
7567 Builder.CreateBr(NewBlocks.front());
7568
7569 // The loop latch must have only one predecessor. Currently it is branched to
7570 // from both the 'then' and 'else' branches.
7571 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7572 NamePrefix + ".pre_latch");
7573
7574 // Ensure that the then block is added to the loop so we add the attributes in
7575 // the next step
7576 L->addBasicBlockToLoop(ThenBlock, LI);
7577}
7578
7579unsigned
7581 const StringMap<bool> &Features) {
7582 if (TargetTriple.isX86()) {
7583 if (Features.lookup("avx512f"))
7584 return 512;
7585 else if (Features.lookup("avx"))
7586 return 256;
7587 return 128;
7588 }
7589 if (TargetTriple.isPPC())
7590 return 128;
7591 if (TargetTriple.isWasm())
7592 return 128;
7593 if (TargetTriple.isSystemZ())
7594 return 64;
7595 return 0;
7596}
7597
7599 MapVector<Value *, Value *> AlignedVars,
7600 Value *IfCond, OrderKind Order,
7601 ConstantInt *Simdlen, ConstantInt *Safelen) {
7602 LLVMContext &Ctx = Builder.getContext();
7603
7604 Function *F = CanonicalLoop->getFunction();
7605
7606 // Blocks must have terminators.
7607 // FIXME: Don't run analyses on incomplete/invalid IR.
7609 for (BasicBlock &BB : *F)
7610 if (!BB.hasTerminator())
7611 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7612
7613 // TODO: We should not rely on pass manager. Currently we use pass manager
7614 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7615 // object. We should have a method which returns all blocks between
7616 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7618 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7619 FAM.registerPass([]() { return LoopAnalysis(); });
7620 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7621
7622 LoopAnalysis LIA;
7623 LoopInfo &&LI = LIA.run(*F, FAM);
7624
7625 for (Instruction *I : UIs)
7626 I->eraseFromParent();
7627
7628 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7629 if (AlignedVars.size()) {
7630 InsertPointTy IP = Builder.saveIP();
7631 for (auto &AlignedItem : AlignedVars) {
7632 Value *AlignedPtr = AlignedItem.first;
7633 Value *Alignment = AlignedItem.second;
7634 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7635 Builder.SetInsertPoint(loadInst->getNextNode());
7636 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7637 Alignment);
7638 }
7639 Builder.restoreIP(IP);
7640 }
7641
7642 if (IfCond) {
7643 ValueToValueMapTy VMap;
7644 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7645 }
7646
7648
7649 // Get the basic blocks from the loop in which memref instructions
7650 // can be found.
7651 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7652 // preferably without running any passes.
7653 for (BasicBlock *Block : L->getBlocks()) {
7654 if (Block == CanonicalLoop->getCond() ||
7655 Block == CanonicalLoop->getHeader())
7656 continue;
7657 Reachable.insert(Block);
7658 }
7659
7660 SmallVector<Metadata *> LoopMDList;
7661
7662 // In presence of finite 'safelen', it may be unsafe to mark all
7663 // the memory instructions parallel, because loop-carried
7664 // dependences of 'safelen' iterations are possible.
7665 // If clause order(concurrent) is specified then the memory instructions
7666 // are marked parallel even if 'safelen' is finite.
7667 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7668 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7669
7670 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7671 // versions so we can't add the loop attributes in that case.
7672 if (IfCond) {
7673 // we can still add llvm.loop.parallel_access
7674 addLoopMetadata(CanonicalLoop, LoopMDList);
7675 return;
7676 }
7677
7678 // Use the above access group metadata to create loop level
7679 // metadata, which should be distinct for each loop.
7680 LoopMDList.push_back(
7681 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7682
7683 if (Simdlen || Safelen) {
7684 // If both simdlen and safelen clauses are specified, the value of the
7685 // simdlen parameter must be less than or equal to the value of the safelen
7686 // parameter. Therefore, use safelen only in the absence of simdlen.
7687 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7688 LoopMDList.push_back(
7689 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7690 ConstantAsMetadata::get(VectorizeWidth)}));
7691 }
7692
7693 addLoopMetadata(CanonicalLoop, LoopMDList);
7694}
7695
7696/// Create the TargetMachine object to query the backend for optimization
7697/// preferences.
7698///
7699/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7700/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7701/// needed for the LLVM pass pipline. We use some default options to avoid
7702/// having to pass too many settings from the frontend that probably do not
7703/// matter.
7704///
7705/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7706/// method. If we are going to use TargetMachine for more purposes, especially
7707/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7708/// might become be worth requiring front-ends to pass on their TargetMachine,
7709/// or at least cache it between methods. Note that while fontends such as Clang
7710/// have just a single main TargetMachine per translation unit, "target-cpu" and
7711/// "target-features" that determine the TargetMachine are per-function and can
7712/// be overrided using __attribute__((target("OPTIONS"))).
7713static std::unique_ptr<TargetMachine>
7715 Module *M = F->getParent();
7716
7717 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7718 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7719 const llvm::Triple &Triple = M->getTargetTriple();
7720
7721 std::string Error;
7723 if (!TheTarget)
7724 return {};
7725
7727 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7728 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7729 /*CodeModel=*/std::nullopt, OptLevel));
7730}
7731
7732/// Heuristically determine the best-performant unroll factor for \p CLI. This
7733/// depends on the target processor. We are re-using the same heuristics as the
7734/// LoopUnrollPass.
7736 Function *F = CLI->getFunction();
7737
7738 // Assume the user requests the most aggressive unrolling, even if the rest of
7739 // the code is optimized using a lower setting.
7741 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7742
7743 // Blocks must have terminators.
7744 // FIXME: Don't run analyses on incomplete/invalid IR.
7746 for (BasicBlock &BB : *F)
7747 if (!BB.hasTerminator())
7748 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7749
7751 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7752 FAM.registerPass([]() { return AssumptionAnalysis(); });
7753 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7754 FAM.registerPass([]() { return LoopAnalysis(); });
7755 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7756 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7757 TargetIRAnalysis TIRA;
7758 if (TM)
7759 TIRA = TargetIRAnalysis(
7760 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7761 FAM.registerPass([&]() { return TIRA; });
7762
7763 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7765 ScalarEvolution &&SE = SEA.run(*F, FAM);
7767 DominatorTree &&DT = DTA.run(*F, FAM);
7768 LoopAnalysis LIA;
7769 LoopInfo &&LI = LIA.run(*F, FAM);
7771 AssumptionCache &&AC = ACT.run(*F, FAM);
7773
7774 for (Instruction *I : UIs)
7775 I->eraseFromParent();
7776
7777 Loop *L = LI.getLoopFor(CLI->getHeader());
7778 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7779
7781 L, SE, TTI,
7782 /*BlockFrequencyInfo=*/nullptr,
7783 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7784 /*UserThreshold=*/std::nullopt,
7785 /*UserAllowPartial=*/true,
7786 /*UserAllowRuntime=*/true,
7787 /*UserUpperBound=*/std::nullopt,
7788 /*UserFullUnrollMaxCount=*/std::nullopt);
7789
7790 UP.Force = true;
7791
7792 // Account for additional optimizations taking place before the LoopUnrollPass
7793 // would unroll the loop.
7796
7797 // Use normal unroll factors even if the rest of the code is optimized for
7798 // size.
7801
7802 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7803 << " Threshold=" << UP.Threshold << "\n"
7804 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7805 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7806 << " PartialOptSizeThreshold="
7807 << UP.PartialOptSizeThreshold << "\n");
7808
7809 // Disable peeling.
7812 /*UserAllowPeeling=*/false,
7813 /*UserAllowProfileBasedPeeling=*/false,
7814 /*UnrollingSpecficValues=*/false);
7815
7817 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7818
7819 // Assume that reads and writes to stack variables can be eliminated by
7820 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7821 // size.
7822 for (BasicBlock *BB : L->blocks()) {
7823 for (Instruction &I : *BB) {
7824 Value *Ptr;
7825 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7826 Ptr = Load->getPointerOperand();
7827 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7828 Ptr = Store->getPointerOperand();
7829 } else
7830 continue;
7831
7832 Ptr = Ptr->stripPointerCasts();
7833
7834 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7835 if (Alloca->getParent() == &F->getEntryBlock())
7836 EphValues.insert(&I);
7837 }
7838 }
7839 }
7840
7841 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7842
7843 // Loop is not unrollable if the loop contains certain instructions.
7844 if (!UCE.canUnroll()) {
7845 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7846 return 1;
7847 }
7848
7849 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7850 << "\n");
7851
7852 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7853 // be able to use it.
7854 int TripCount = 0;
7855 int MaxTripCount = 0;
7856 bool MaxOrZero = false;
7857 unsigned TripMultiple = 0;
7858
7859 unsigned Factor =
7860 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7861 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7862 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7863
7864 // This function returns 1 to signal to not unroll a loop.
7865 if (Factor == 0)
7866 return 1;
7867 return Factor;
7868}
7869
7871 int32_t Factor,
7872 CanonicalLoopInfo **UnrolledCLI) {
7873 assert(Factor >= 0 && "Unroll factor must not be negative");
7874
7875 Function *F = Loop->getFunction();
7876 LLVMContext &Ctx = F->getContext();
7877
7878 // If the unrolled loop is not used for another loop-associated directive, it
7879 // is sufficient to add metadata for the LoopUnrollPass.
7880 if (!UnrolledCLI) {
7881 SmallVector<Metadata *, 2> LoopMetadata;
7882 LoopMetadata.push_back(
7883 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7884
7885 if (Factor >= 1) {
7887 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7888 LoopMetadata.push_back(MDNode::get(
7889 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7890 }
7891
7892 addLoopMetadata(Loop, LoopMetadata);
7893 return;
7894 }
7895
7896 // Heuristically determine the unroll factor.
7897 if (Factor == 0)
7899
7900 // No change required with unroll factor 1.
7901 if (Factor == 1) {
7902 *UnrolledCLI = Loop;
7903 return;
7904 }
7905
7906 assert(Factor >= 2 &&
7907 "unrolling only makes sense with a factor of 2 or larger");
7908
7909 Type *IndVarTy = Loop->getIndVarType();
7910
7911 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7912 // unroll the inner loop.
7913 Value *FactorVal =
7914 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7915 /*isSigned=*/false));
7916 std::vector<CanonicalLoopInfo *> LoopNest =
7917 tileLoops(DL, {Loop}, {FactorVal});
7918 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7919 *UnrolledCLI = LoopNest[0];
7920 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7921
7922 // LoopUnrollPass can only fully unroll loops with constant trip count.
7923 // Unroll by the unroll factor with a fallback epilog for the remainder
7924 // iterations if necessary.
7926 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7928 InnerLoop,
7929 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7931 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
7932
7933#ifndef NDEBUG
7934 (*UnrolledCLI)->assertOK();
7935#endif
7936}
7937
7940 llvm::Value *BufSize, llvm::Value *CpyBuf,
7941 llvm::Value *CpyFn, llvm::Value *DidIt) {
7942 if (!updateToLocation(Loc))
7943 return Loc.IP;
7944
7945 uint32_t SrcLocStrSize;
7946 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7947 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7948 Value *ThreadId = getOrCreateThreadID(Ident);
7949
7950 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
7951
7952 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
7953
7954 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
7955 createRuntimeFunctionCall(Fn, Args);
7956
7957 return Builder.saveIP();
7958}
7959
7961 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7962 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
7964
7965 if (!updateToLocation(Loc))
7966 return Loc.IP;
7967
7968 // If needed allocate and initialize `DidIt` with 0.
7969 // DidIt: flag variable: 1=single thread; 0=not single thread.
7970 llvm::Value *DidIt = nullptr;
7971 if (!CPVars.empty()) {
7972 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
7973 Builder.CreateStore(Builder.getInt32(0), DidIt);
7974 }
7975
7976 Directive OMPD = Directive::OMPD_single;
7977 uint32_t SrcLocStrSize;
7978 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7979 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7980 Value *ThreadId = getOrCreateThreadID(Ident);
7981 Value *Args[] = {Ident, ThreadId};
7982
7983 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
7984 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
7985
7986 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
7987 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
7988
7989 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
7990 if (Error Err = FiniCB(IP))
7991 return Err;
7992
7993 // The thread that executes the single region must set `DidIt` to 1.
7994 // This is used by __kmpc_copyprivate, to know if the caller is the
7995 // single thread or not.
7996 if (DidIt)
7997 Builder.CreateStore(Builder.getInt32(1), DidIt);
7998
7999 return Error::success();
8000 };
8001
8002 // generates the following:
8003 // if (__kmpc_single()) {
8004 // .... single region ...
8005 // __kmpc_end_single
8006 // }
8007 // __kmpc_copyprivate
8008 // __kmpc_barrier
8009
8010 InsertPointOrErrorTy AfterIP =
8011 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8012 /*Conditional*/ true,
8013 /*hasFinalize*/ true);
8014 if (!AfterIP)
8015 return AfterIP.takeError();
8016
8017 if (DidIt) {
8018 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8019 // NOTE BufSize is currently unused, so just pass 0.
8021 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8022 CPFuncs[I], DidIt);
8023 // NOTE __kmpc_copyprivate already inserts a barrier
8024 } else if (!IsNowait) {
8025 InsertPointOrErrorTy AfterIP =
8027 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8028 /* CheckCancelFlag */ false);
8029 if (!AfterIP)
8030 return AfterIP.takeError();
8031 }
8032 return Builder.saveIP();
8033}
8034
8037 BodyGenCallbackTy BodyGenCB,
8038 FinalizeCallbackTy FiniCB, bool IsNowait) {
8039
8040 if (!updateToLocation(Loc))
8041 return Loc.IP;
8042
8043 // All threads execute the scope body — no conditional entry.
8044 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8045 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8046 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8047 /*IsCancellable=*/false);
8048 if (!AfterIP)
8049 return AfterIP.takeError();
8050
8051 Builder.restoreIP(*AfterIP);
8052 if (!IsNowait) {
8053 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8054 omp::Directive::OMPD_unknown,
8055 /*ForceSimpleCall=*/false,
8056 /*CheckCancelFlag=*/false);
8057 if (!AfterIP)
8058 return AfterIP.takeError();
8059 }
8060 return Builder.saveIP();
8061}
8062
8064 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8065 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8066
8067 if (!updateToLocation(Loc))
8068 return Loc.IP;
8069
8070 Directive OMPD = Directive::OMPD_critical;
8071 uint32_t SrcLocStrSize;
8072 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8073 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8074 Value *ThreadId = getOrCreateThreadID(Ident);
8075 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8076 Value *Args[] = {Ident, ThreadId, LockVar};
8077
8078 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8079 Function *RTFn = nullptr;
8080 if (HintInst) {
8081 // Add Hint to entry Args and create call
8082 EnterArgs.push_back(HintInst);
8083 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8084 } else {
8085 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8086 }
8087 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8088
8089 Function *ExitRTLFn =
8090 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8091 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8092
8093 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8094 /*Conditional*/ false, /*hasFinalize*/ true);
8095}
8096
8099 InsertPointTy AllocaIP, unsigned NumLoops,
8100 ArrayRef<llvm::Value *> StoreValues,
8101 const Twine &Name, bool IsDependSource) {
8102 assert(
8103 llvm::all_of(StoreValues,
8104 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8105 "OpenMP runtime requires depend vec with i64 type");
8106
8107 if (!updateToLocation(Loc))
8108 return Loc.IP;
8109
8110 // Allocate space for vector and generate alloc instruction.
8111 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8112 Builder.restoreIP(AllocaIP);
8113 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8114 ArgsBase->setAlignment(Align(8));
8116
8117 // Store the index value with offset in depend vector.
8118 for (unsigned I = 0; I < NumLoops; ++I) {
8119 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8120 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8121 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8122 STInst->setAlignment(Align(8));
8123 }
8124
8125 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8126 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8127
8128 uint32_t SrcLocStrSize;
8129 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8130 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8131 Value *ThreadId = getOrCreateThreadID(Ident);
8132 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8133
8134 Function *RTLFn = nullptr;
8135 if (IsDependSource)
8136 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8137 else
8138 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8139 createRuntimeFunctionCall(RTLFn, Args);
8140
8141 return Builder.saveIP();
8142}
8143
8145 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8146 FinalizeCallbackTy FiniCB, bool IsThreads) {
8147 if (!updateToLocation(Loc))
8148 return Loc.IP;
8149
8150 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8151 Instruction *EntryCall = nullptr;
8152 Instruction *ExitCall = nullptr;
8153
8154 if (IsThreads) {
8155 uint32_t SrcLocStrSize;
8156 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8157 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8158 Value *ThreadId = getOrCreateThreadID(Ident);
8159 Value *Args[] = {Ident, ThreadId};
8160
8161 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8162 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8163
8164 Function *ExitRTLFn =
8165 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8166 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8167 }
8168
8169 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8170 /*Conditional*/ false, /*hasFinalize*/ true);
8171}
8172
8173OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8174 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8175 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8176 bool HasFinalize, bool IsCancellable) {
8177
8178 if (HasFinalize)
8179 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8180
8181 // Create inlined region's entry and body blocks, in preparation
8182 // for conditional creation
8183 BasicBlock *EntryBB = Builder.GetInsertBlock();
8184 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8186 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8187 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8188 BasicBlock *FiniBB =
8189 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8190
8191 Builder.SetInsertPoint(EntryBB->getTerminator());
8192 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8193
8194 // generate body
8195 if (Error Err =
8196 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8197 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8198 return Err;
8199
8200 // emit exit call and do any needed finalization.
8201 auto FinIP = InsertPointTy(FiniBB, FiniBB->getFirstInsertionPt());
8202 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8203 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8204 "Unexpected control flow graph state!!");
8205 InsertPointOrErrorTy AfterIP =
8206 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8207 if (!AfterIP)
8208 return AfterIP.takeError();
8209
8210 // If we are skipping the region of a non conditional, remove the exit
8211 // block, and clear the builder's insertion point.
8212 assert(SplitPos->getParent() == ExitBB &&
8213 "Unexpected Insertion point location!");
8214 auto merged = MergeBlockIntoPredecessor(ExitBB);
8215 BasicBlock *ExitPredBB = SplitPos->getParent();
8216 auto InsertBB = merged ? ExitPredBB : ExitBB;
8218 SplitPos->eraseFromParent();
8219 Builder.SetInsertPoint(InsertBB);
8220
8221 return Builder.saveIP();
8222}
8223
8224OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8225 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8226 // if nothing to do, Return current insertion point.
8227 if (!Conditional || !EntryCall)
8228 return Builder.saveIP();
8229
8230 BasicBlock *EntryBB = Builder.GetInsertBlock();
8231 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8232 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8233 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8234
8235 // Emit thenBB and set the Builder's insertion point there for
8236 // body generation next. Place the block after the current block.
8237 Function *CurFn = EntryBB->getParent();
8238 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8239
8240 // Move Entry branch to end of ThenBB, and replace with conditional
8241 // branch (If-stmt)
8242 Instruction *EntryBBTI = EntryBB->getTerminator();
8243 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8244 EntryBBTI->removeFromParent();
8245 Builder.SetInsertPoint(UI);
8246 Builder.Insert(EntryBBTI);
8247 UI->eraseFromParent();
8248 Builder.SetInsertPoint(ThenBB->getTerminator());
8249
8250 // return an insertion point to ExitBB.
8251 return IRBuilder<>::InsertPoint(ExitBB, ExitBB->getFirstInsertionPt());
8252}
8253
8254OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8255 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8256 bool HasFinalize) {
8257
8258 Builder.restoreIP(FinIP);
8259
8260 // If there is finalization to do, emit it before the exit call
8261 if (HasFinalize) {
8262 assert(!FinalizationStack.empty() &&
8263 "Unexpected finalization stack state!");
8264
8265 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8266 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8267
8268 if (Error Err = Fi.mergeFiniBB(Builder, FinIP.getBlock()))
8269 return std::move(Err);
8270
8271 // Exit condition: insertion point is before the terminator of the new Fini
8272 // block
8273 Builder.SetInsertPoint(FinIP.getBlock()->getTerminator());
8274 }
8275
8276 if (!ExitCall)
8277 return Builder.saveIP();
8278
8279 // place the Exitcall as last instruction before Finalization block terminator
8280 ExitCall->removeFromParent();
8281 Builder.Insert(ExitCall);
8282
8283 return IRBuilder<>::InsertPoint(ExitCall->getParent(),
8284 ExitCall->getIterator());
8285}
8286
8288 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8289 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8290 if (!IP.isSet())
8291 return IP;
8292
8294
8295 // creates the following CFG structure
8296 // OMP_Entry : (MasterAddr != PrivateAddr)?
8297 // F T
8298 // | \
8299 // | copin.not.master
8300 // | /
8301 // v /
8302 // copyin.not.master.end
8303 // |
8304 // v
8305 // OMP.Entry.Next
8306
8307 BasicBlock *OMP_Entry = IP.getBlock();
8308 Function *CurFn = OMP_Entry->getParent();
8309 BasicBlock *CopyBegin =
8310 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8311 BasicBlock *CopyEnd = nullptr;
8312
8313 // If entry block is terminated, split to preserve the branch to following
8314 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8316 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8317 "copyin.not.master.end");
8318 OMP_Entry->getTerminator()->eraseFromParent();
8319 } else {
8320 CopyEnd =
8321 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8322 }
8323
8324 Builder.SetInsertPoint(OMP_Entry);
8325 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8326 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8327 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8328 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8329
8330 Builder.SetInsertPoint(CopyBegin);
8331 if (BranchtoEnd)
8332 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8333
8334 return Builder.saveIP();
8335}
8336
8338 Value *Size, Value *Allocator,
8339 std::string Name) {
8341 if (!updateToLocation(Loc))
8342 return nullptr;
8343
8344 uint32_t SrcLocStrSize;
8345 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8346 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8347 Value *ThreadId = getOrCreateThreadID(Ident);
8348 Value *Args[] = {ThreadId, Size, Allocator};
8349
8350 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8351
8352 return createRuntimeFunctionCall(Fn, Args, Name);
8353}
8354
8356 Value *Align, Value *Size,
8357 Value *Allocator,
8358 std::string Name) {
8360 if (!updateToLocation(Loc))
8361 return nullptr;
8362
8363 uint32_t SrcLocStrSize;
8364 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8365 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8366 Value *ThreadId = getOrCreateThreadID(Ident);
8367 Value *Args[] = {ThreadId, Align, Size, Allocator};
8368
8369 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8370
8371 return Builder.CreateCall(Fn, Args, Name);
8372}
8373
8375 Value *Addr, Value *Allocator,
8376 std::string Name) {
8378 if (!updateToLocation(Loc))
8379 return nullptr;
8380
8381 uint32_t SrcLocStrSize;
8382 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8383 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8384 Value *ThreadId = getOrCreateThreadID(Ident);
8385 Value *Args[] = {ThreadId, Addr, Allocator};
8386 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8387 return createRuntimeFunctionCall(Fn, Args, Name);
8388}
8389
8391 Value *Size,
8392 const Twine &Name) {
8395
8396 Value *Args[] = {Size};
8397 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8398 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8400 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8401 return Call;
8402}
8403
8405 Type *VarType,
8406 const Twine &Name) {
8407 return createOMPAllocShared(
8408 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8409}
8410
8412 Value *Addr, Value *Size,
8413 const Twine &Name) {
8416
8417 Value *Args[] = {Addr, Size};
8418 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8419 return Builder.CreateCall(Fn, Args, Name);
8420}
8421
8423 Value *Addr, Type *VarType,
8424 const Twine &Name) {
8425 return createOMPFreeShared(
8426 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8427 Name);
8428}
8429
8431 const LocationDescription &Loc, Value *InteropVar,
8433 Value *DependenceAddress, bool HaveNowaitClause) {
8436
8437 uint32_t SrcLocStrSize;
8438 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8439 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8440 Value *ThreadId = getOrCreateThreadID(Ident);
8441 if (Device == nullptr)
8443 else if (Device->getType() != Int32)
8444 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8445 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8446 if (NumDependences == nullptr) {
8447 NumDependences = ConstantInt::get(Int32, 0);
8448 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8449 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8450 }
8451 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8452 Value *Args[] = {
8453 Ident, ThreadId, InteropVar, InteropTypeVal,
8454 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8455
8456 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8457
8458 return createRuntimeFunctionCall(Fn, Args);
8459}
8460
8462 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8463 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8466
8467 uint32_t SrcLocStrSize;
8468 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8469 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8470 Value *ThreadId = getOrCreateThreadID(Ident);
8471 if (Device == nullptr)
8473 else if (Device->getType() != Int32)
8474 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8475 if (NumDependences == nullptr) {
8476 NumDependences = ConstantInt::get(Int32, 0);
8477 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8478 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8479 }
8480 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8481 Value *Args[] = {
8482 Ident, ThreadId, InteropVar, Device,
8483 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8484
8485 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8486
8487 return createRuntimeFunctionCall(Fn, Args);
8488}
8489
8491 Value *InteropVar, Value *Device,
8492 Value *NumDependences,
8493 Value *DependenceAddress,
8494 bool HaveNowaitClause) {
8497 uint32_t SrcLocStrSize;
8498 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8499 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8500 Value *ThreadId = getOrCreateThreadID(Ident);
8501 if (Device == nullptr)
8503 else if (Device->getType() != Int32)
8504 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8505 if (NumDependences == nullptr) {
8506 NumDependences = ConstantInt::get(Int32, 0);
8507 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8508 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8509 }
8510 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8511 Value *Args[] = {
8512 Ident, ThreadId, InteropVar, Device,
8513 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8514
8515 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8516
8517 return createRuntimeFunctionCall(Fn, Args);
8518}
8519
8522 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8525
8526 uint32_t SrcLocStrSize;
8527 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8528 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8529 Value *ThreadId = getOrCreateThreadID(Ident);
8530 Constant *ThreadPrivateCache =
8531 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8532 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8533
8534 Function *Fn =
8535 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8536
8537 return createRuntimeFunctionCall(Fn, Args);
8538}
8539
8541 const LocationDescription &Loc,
8543 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8544 "expected num_threads and num_teams to be specified");
8545
8546 if (!updateToLocation(Loc))
8547 return Loc.IP;
8548
8549 uint32_t SrcLocStrSize;
8550 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8551 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8552 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8553 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8554 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8555 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8556 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8557 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8558
8559 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8560 Function *Kernel = DebugKernelWrapper;
8561
8562 // We need to strip the debug prefix to get the correct kernel name.
8563 StringRef KernelName = Kernel->getName();
8564 const std::string DebugPrefix = "_debug__";
8565 if (KernelName.ends_with(DebugPrefix)) {
8566 KernelName = KernelName.drop_back(DebugPrefix.length());
8567 Kernel = M.getFunction(KernelName);
8568 assert(Kernel && "Expected the real kernel to exist");
8569 }
8570
8571 // Manifest the launch configuration in the metadata matching the kernel
8572 // environment.
8573 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8574 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8575 Attrs.MaxTeams.front());
8576
8577 // If MaxThreads is not set and needs adjustment, select the maximum between
8578 // the default workgroup size and the MinThreads value.
8579 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8580 if (MaxThreadsVal < 0 && UseDefaultMaxThreads) {
8581 if (hasGridValue(T)) {
8582 MaxThreadsVal =
8583 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8584 Attrs.MinThreads.front());
8585 } else {
8586 MaxThreadsVal = Attrs.MinThreads.front();
8587 }
8588 }
8589
8590 if (MaxThreadsVal > 0)
8591 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8592 MaxThreadsVal);
8593
8594 Constant *MinThreads =
8595 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8596 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8597 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8598 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8599 Constant *ReductionDataSize =
8600 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8601
8603 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8604 const DataLayout &DL = Fn->getDataLayout();
8605
8606 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8607 Constant *DynamicEnvironmentInitializer =
8608 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8609 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8610 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8611 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8612 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8613 DL.getDefaultGlobalsAddressSpace());
8614 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8615
8616 Constant *DynamicEnvironment =
8617 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8618 ? DynamicEnvironmentGV
8619 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8620 DynamicEnvironmentPtr);
8621
8622 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8623 ConfigurationEnvironment, {
8624 UseGenericStateMachineVal,
8625 MayUseNestedParallelismVal,
8626 IsSPMDVal,
8627 MinThreads,
8628 MaxThreads,
8629 MinTeams,
8630 MaxTeams,
8631 ReductionDataSize,
8632 });
8633 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8634 KernelEnvironment, {
8635 ConfigurationEnvironmentInitializer,
8636 Ident,
8637 DynamicEnvironment,
8638 });
8639 std::string KernelEnvironmentName =
8640 (KernelName + "_kernel_environment").str();
8641 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8642 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8643 KernelEnvironmentInitializer, KernelEnvironmentName,
8644 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8645 DL.getDefaultGlobalsAddressSpace());
8646 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8647
8648 Constant *KernelEnvironment =
8649 KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8650 ? KernelEnvironmentGV
8651 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8652 KernelEnvironmentPtr);
8653 Value *KernelLaunchEnvironment =
8654 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8655 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8656 KernelLaunchEnvironment =
8657 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8658 ? KernelLaunchEnvironment
8659 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8660 KernelLaunchEnvParamTy);
8661 CallInst *ThreadKind = createRuntimeFunctionCall(
8662 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8663
8664 Value *ExecUserCode = Builder.CreateICmpEQ(
8665 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8666 "exec_user_code");
8667
8668 // ThreadKind = __kmpc_target_init(...)
8669 // if (ThreadKind == -1)
8670 // user_code
8671 // else
8672 // return;
8673
8674 auto *UI = Builder.CreateUnreachable();
8675 BasicBlock *CheckBB = UI->getParent();
8676 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8677
8678 BasicBlock *WorkerExitBB = BasicBlock::Create(
8679 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8680 Builder.SetInsertPoint(WorkerExitBB);
8681 Builder.CreateRetVoid();
8682
8683 auto *CheckBBTI = CheckBB->getTerminator();
8684 Builder.SetInsertPoint(CheckBBTI);
8685 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8686
8687 CheckBBTI->eraseFromParent();
8688 UI->eraseFromParent();
8689
8690 // Continue in the "user_code" block, see diagram above and in
8691 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8692 return InsertPointTy(UserCodeEntryBB, UserCodeEntryBB->getFirstInsertionPt());
8693}
8694
8696 int32_t TeamsReductionDataSize) {
8697 if (!updateToLocation(Loc))
8698 return;
8699
8701 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8702
8704
8705 if (!TeamsReductionDataSize)
8706 return;
8707
8708 Function *Kernel = Builder.GetInsertBlock()->getParent();
8709 // We need to strip the debug prefix to get the correct kernel name.
8710 StringRef KernelName = Kernel->getName();
8711 const std::string DebugPrefix = "_debug__";
8712 if (KernelName.ends_with(DebugPrefix))
8713 KernelName = KernelName.drop_back(DebugPrefix.length());
8714 auto *KernelEnvironmentGV =
8715 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8716 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8717 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8718 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8719 KernelEnvironmentInitializer,
8720 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8721 KernelEnvironmentGV->setInitializer(NewInitializer);
8722}
8723
8724static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8725 bool Min) {
8726 if (Kernel.hasFnAttribute(Name)) {
8727 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8728 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8729 }
8730 Kernel.addFnAttr(Name, llvm::utostr(Value));
8731}
8732
8733std::pair<int32_t, int32_t>
8735 int32_t ThreadLimit =
8736 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8737
8738 if (T.isAMDGPU()) {
8739 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8740 if (!Attr.isValid() || !Attr.isStringAttribute())
8741 return {0, ThreadLimit};
8742 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8743 int32_t LB, UB;
8744 if (!llvm::to_integer(UBStr, UB, 10))
8745 return {0, ThreadLimit};
8746 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8747 if (!llvm::to_integer(LBStr, LB, 10))
8748 return {0, UB};
8749 return {LB, UB};
8750 }
8751
8752 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8753 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8754 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8755 }
8756 return {0, ThreadLimit};
8757}
8758
8760 Function &Kernel, int32_t LB,
8761 int32_t UB) {
8762 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8763
8764 if (T.isAMDGPU()) {
8765 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8766 llvm::utostr(LB) + "," + llvm::utostr(UB));
8767 return;
8768 }
8769
8771}
8772
8773std::pair<int32_t, int32_t>
8775 // TODO: Read from backend annotations if available.
8776 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8777}
8778
8780 int32_t LB, int32_t UB) {
8781 if (UB > 0) {
8782 if (T.isNVPTX())
8784 if (T.isAMDGPU())
8785 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8786 }
8787
8788 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8789}
8790
8791void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8792 Function *OutlinedFn) {
8793 if (Config.isTargetDevice()) {
8795 // TODO: Determine if DSO local can be set to true.
8796 OutlinedFn->setDSOLocal(false);
8798 if (T.isAMDGCN())
8800 else if (T.isNVPTX())
8802 else if (T.isSPIRV())
8804 }
8805}
8806
8807Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8808 StringRef EntryFnIDName) {
8809 if (Config.isTargetDevice()) {
8810 assert(OutlinedFn && "The outlined function must exist if embedded");
8811 return OutlinedFn;
8812 }
8813
8814 return new GlobalVariable(
8815 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8816 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8817}
8818
8819Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8820 StringRef EntryFnName) {
8821 if (OutlinedFn)
8822 return OutlinedFn;
8823
8824 assert(!M.getGlobalVariable(EntryFnName, true) &&
8825 "Named kernel already exists?");
8826 return new GlobalVariable(
8827 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8828 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8829}
8830
8832 TargetRegionEntryInfo &EntryInfo,
8833 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8834 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8835
8836 SmallString<64> EntryFnName;
8837 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8838
8839 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8840 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8841 if (!CBResult)
8842 return CBResult.takeError();
8843 OutlinedFn = *CBResult;
8844 } else {
8845 OutlinedFn = nullptr;
8846 }
8847
8848 // If this target outline function is not an offload entry, we don't need to
8849 // register it. This may be in the case of a false if clause, or if there are
8850 // no OpenMP targets.
8851 if (!IsOffloadEntry)
8852 return Error::success();
8853
8854 std::string EntryFnIDName =
8855 Config.isTargetDevice()
8856 ? std::string(EntryFnName)
8857 : createPlatformSpecificName({EntryFnName, "region_id"});
8858
8859 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8860 EntryFnName, EntryFnIDName);
8861 return Error::success();
8862}
8863
8865 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8866 StringRef EntryFnName, StringRef EntryFnIDName) {
8867 if (OutlinedFn)
8868 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8869 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8870 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8871 OffloadInfoManager.registerTargetRegionEntryInfo(
8872 EntryInfo, EntryAddr, OutlinedFnID,
8874 return OutlinedFnID;
8875}
8876
8878 const LocationDescription &Loc, InsertPointTy AllocaIP,
8879 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8880 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8881 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8882 omp::RuntimeFunction *MapperFunc,
8884 BodyGenTy BodyGenType)>
8885 BodyGenCB,
8886 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8887 if (!updateToLocation(Loc))
8888 return InsertPointTy();
8889
8890 Builder.restoreIP(CodeGenIP);
8891
8892 bool IsStandAlone = !BodyGenCB;
8893 MapInfosTy *MapInfo;
8894 // Generate the code for the opening of the data environment. Capture all the
8895 // arguments of the runtime call by reference because they are used in the
8896 // closing of the region.
8897 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8898 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8899 MapInfo = &GenMapInfoCB(Builder.saveIP());
8900 if (Error Err = emitOffloadingArrays(
8901 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
8902 /*IsNonContiguous=*/true, DeviceAddrCB))
8903 return Err;
8904
8905 TargetDataRTArgs RTArgs;
8907
8908 // Emit the number of elements in the offloading arrays.
8909 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8910
8911 // Source location for the ident struct
8912 if (!SrcLocInfo) {
8913 uint32_t SrcLocStrSize;
8914 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8915 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8916 }
8917
8918 SmallVector<llvm::Value *, 13> OffloadingArgs = {
8919 SrcLocInfo, DeviceID,
8920 PointerNum, RTArgs.BasePointersArray,
8921 RTArgs.PointersArray, RTArgs.SizesArray,
8922 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8923 RTArgs.MappersArray};
8924
8925 if (IsStandAlone) {
8926 assert(MapperFunc && "MapperFunc missing for standalone target data");
8927
8928 auto TaskBodyCB = [&](Value *, Value *,
8930 if (Info.HasNoWait) {
8931 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
8935 }
8936
8938 OffloadingArgs);
8939
8940 if (Info.HasNoWait) {
8941 BasicBlock *OffloadContBlock =
8942 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
8943 Function *CurFn = Builder.GetInsertBlock()->getParent();
8944 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
8945 Builder.restoreIP(Builder.saveIP());
8946 }
8947 return Error::success();
8948 };
8949
8950 bool RequiresOuterTargetTask = Info.HasNoWait;
8951 if (!RequiresOuterTargetTask)
8952 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
8953 /*TargetTaskAllocaIP=*/{}));
8954 else
8955 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
8956 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
8957 } else {
8958 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
8959 omp::OMPRTL___tgt_target_data_begin_mapper);
8960
8961 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
8962
8963 for (auto DeviceMap : Info.DevicePtrInfoMap) {
8964 if (isa<AllocaInst>(DeviceMap.second.second)) {
8965 auto *LI =
8966 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
8967 Builder.CreateStore(LI, DeviceMap.second.second);
8968 }
8969 }
8970
8971 // If device pointer privatization is required, emit the body of the
8972 // region here. It will have to be duplicated: with and without
8973 // privatization.
8974 InsertPointOrErrorTy AfterIP =
8975 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
8976 if (!AfterIP)
8977 return AfterIP.takeError();
8978 Builder.restoreIP(*AfterIP);
8979 }
8980 return Error::success();
8981 };
8982
8983 // If we need device pointer privatization, we need to emit the body of the
8984 // region with no privatization in the 'else' branch of the conditional.
8985 // Otherwise, we don't have to do anything.
8986 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8987 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8988 InsertPointOrErrorTy AfterIP =
8989 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
8990 if (!AfterIP)
8991 return AfterIP.takeError();
8992 Builder.restoreIP(*AfterIP);
8993 return Error::success();
8994 };
8995
8996 // Generate code for the closing of the data region.
8997 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8998 ArrayRef<BasicBlock *> DeallocBlocks) {
8999 TargetDataRTArgs RTArgs;
9000 Info.EmitDebug = !MapInfo->Names.empty();
9001 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
9002
9003 // Emit the number of elements in the offloading arrays.
9004 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9005
9006 // Source location for the ident struct
9007 if (!SrcLocInfo) {
9008 uint32_t SrcLocStrSize;
9009 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9010 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9011 }
9012
9013 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9014 PointerNum, RTArgs.BasePointersArray,
9015 RTArgs.PointersArray, RTArgs.SizesArray,
9016 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9017 RTArgs.MappersArray};
9018 Function *EndMapperFunc =
9019 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9020
9021 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9022 return Error::success();
9023 };
9024
9025 // We don't have to do anything to close the region if the if clause evaluates
9026 // to false.
9027 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9028 ArrayRef<BasicBlock *> DeallocBlocks) {
9029 return Error::success();
9030 };
9031
9032 Error Err = [&]() -> Error {
9033 if (BodyGenCB) {
9034 Error Err = [&]() {
9035 if (IfCond)
9036 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9037 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9038 }();
9039
9040 if (Err)
9041 return Err;
9042
9043 // If we don't require privatization of device pointers, we emit the body
9044 // in between the runtime calls. This avoids duplicating the body code.
9045 InsertPointOrErrorTy AfterIP =
9046 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9047 if (!AfterIP)
9048 return AfterIP.takeError();
9049 restoreIPandDebugLoc(Builder, *AfterIP);
9050
9051 if (IfCond)
9052 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9053 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9054 }
9055 if (IfCond)
9056 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9057 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9058 }();
9059
9060 if (Err)
9061 return Err;
9062
9063 return Builder.saveIP();
9064}
9065
9068 bool IsGPUDistribute) {
9069 assert((IVSize == 32 || IVSize == 64) &&
9070 "IV size is not compatible with the omp runtime");
9071 RuntimeFunction Name;
9072 if (IsGPUDistribute)
9073 Name = IVSize == 32
9074 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9075 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9076 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9077 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9078 else
9079 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9080 : omp::OMPRTL___kmpc_for_static_init_4u)
9081 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9082 : omp::OMPRTL___kmpc_for_static_init_8u);
9083
9084 return getOrCreateRuntimeFunction(M, Name);
9085}
9086
9088 bool IVSigned) {
9089 assert((IVSize == 32 || IVSize == 64) &&
9090 "IV size is not compatible with the omp runtime");
9091 RuntimeFunction Name = IVSize == 32
9092 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9093 : omp::OMPRTL___kmpc_dispatch_init_4u)
9094 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9095 : omp::OMPRTL___kmpc_dispatch_init_8u);
9096
9097 return getOrCreateRuntimeFunction(M, Name);
9098}
9099
9101 bool IVSigned) {
9102 assert((IVSize == 32 || IVSize == 64) &&
9103 "IV size is not compatible with the omp runtime");
9104 RuntimeFunction Name = IVSize == 32
9105 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9106 : omp::OMPRTL___kmpc_dispatch_next_4u)
9107 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9108 : omp::OMPRTL___kmpc_dispatch_next_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_fini_4
9119 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9120 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9121 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9122
9123 return getOrCreateRuntimeFunction(M, Name);
9124}
9125
9127 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9128}
9129
9131 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9132 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9133
9134 DISubprogram *NewSP = Func->getSubprogram();
9135 if (!NewSP)
9136 return;
9137
9139
9140 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9141 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9142 // Only use cached variable if the arg number matches. This is important
9143 // so that DIVariable created for privatized variables are not discarded.
9144 if (NewVar && (arg == NewVar->getArg()))
9145 return NewVar;
9146
9148 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9149 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9150 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9151 return NewVar;
9152 };
9153
9154 auto UpdateDebugRecord = [&](auto *DR) {
9155 DILocalVariable *OldVar = DR->getVariable();
9156 unsigned ArgNo = 0;
9157 for (auto Loc : DR->location_ops()) {
9158 auto Iter = ValueReplacementMap.find(Loc);
9159 if (Iter != ValueReplacementMap.end()) {
9160 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9161 ArgNo = std::get<1>(Iter->second) + 1;
9162 }
9163 }
9164 if (ArgNo != 0)
9165 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9166 };
9167
9169 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9170 if (DVR->getNumVariableLocationOps() != 1u) {
9171 DVR->setKillLocation();
9172 return;
9173 }
9174 Value *Loc = DVR->getVariableLocationOp(0u);
9175 BasicBlock *CurBB = DVR->getParent();
9176 BasicBlock *RequiredBB = nullptr;
9177
9178 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9179 RequiredBB = LocInst->getParent();
9180 else if (isa<llvm::Argument>(Loc))
9181 RequiredBB = &DVR->getFunction()->getEntryBlock();
9182
9183 if (RequiredBB && RequiredBB != CurBB) {
9184 assert(!RequiredBB->empty());
9185 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9186 RequiredBB->back().getIterator());
9187 DVRsToDelete.push_back(DVR);
9188 }
9189 };
9190
9191 // The location and scope of variable intrinsics and records still point to
9192 // the parent function of the target region. Update them.
9193 for (Instruction &I : instructions(Func)) {
9195 "Unexpected debug intrinsic");
9196 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9197 UpdateDebugRecord(&DVR);
9198 MoveDebugRecordToCorrectBlock(&DVR);
9199 }
9200 }
9201 for (auto *DVR : DVRsToDelete)
9202 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9203 // An extra argument is passed to the device. Create the debug data for it.
9204 if (OMPBuilder.Config.isTargetDevice()) {
9205 DICompileUnit *CU = NewSP->getUnit();
9206 Module *M = Func->getParent();
9207 DIBuilder DB(*M, true, CU);
9208 DIType *VoidPtrTy =
9209 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9210 unsigned ArgNo = Func->arg_size();
9211 DILocalVariable *Var = DB.createParameterVariable(
9212 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9213 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9214 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9215 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9216 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9217 &(*Func->begin()));
9218 }
9219}
9220
9222 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9223 return cast<Operator>(V)->getOperand(0);
9224 return V;
9225}
9226
9228 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9230 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9233 DebugLoc OutlinedFnLoc) {
9234 SmallVector<Type *> ParameterTypes;
9235 if (OMPBuilder.Config.isTargetDevice()) {
9236 // All parameters to target devices are passed as pointers
9237 // or i64. This assumes 64-bit address spaces/pointers.
9238 for (auto &Arg : Inputs)
9239 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9240 ? Arg->getType()
9241 : Type::getInt64Ty(Builder.getContext()));
9242 } else {
9243 for (auto &Arg : Inputs)
9244 ParameterTypes.push_back(Arg->getType());
9245 }
9246
9247 // The implicit dyn_ptr argument is always the last parameter on both host
9248 // and device so the argument counts match without runtime manipulation.
9249 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9250 ParameterTypes.push_back(PtrTy);
9251
9252 auto BB = Builder.GetInsertBlock();
9253 auto M = BB->getModule();
9254 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9255 /*isVarArg*/ false);
9256 auto Func =
9257 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9258
9259 // Forward target-cpu and target-features function attributes from the
9260 // original function to the new outlined function.
9261 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9262
9263 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9264 if (TargetCpuAttr.isStringAttribute())
9265 Func->addFnAttr(TargetCpuAttr);
9266
9267 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9268 if (TargetFeaturesAttr.isStringAttribute())
9269 Func->addFnAttr(TargetFeaturesAttr);
9270
9271 if (OMPBuilder.Config.isTargetDevice()) {
9272 Value *ExecMode =
9273 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9274 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9275 }
9276
9277 // Save insert point.
9278 IRBuilder<>::InsertPointGuard IPG(Builder);
9279 // We will generate the entries in the outlined function but the debug
9280 // location is still pointing to the parent function, which is the wrong
9281 // scope. OutlinedFnLoc, when the caller provides one, is the same source
9282 // position scoped to the subprogram that will be attached to the outlined
9283 // function, so it is what everything emitted below needs.
9284 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9285
9286 // Generate the region into the function.
9287 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9288 Builder.SetInsertPoint(EntryBB);
9289
9290 // Insert target init call in the device compilation pass.
9291 if (OMPBuilder.Config.isTargetDevice())
9292 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9293
9294 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9295
9296 // As we embed the user code in the middle of our target region after we
9297 // generate entry code, we must move what allocas we can into the entry
9298 // block to avoid possible breaking optimisations for device
9299 if (OMPBuilder.Config.isTargetDevice())
9301
9302 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9303 BasicBlock *OutlinedBodyBB =
9304 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9306 Builder.saveIP(),
9307 OpenMPIRBuilder::InsertPointTy(OutlinedBodyBB, OutlinedBodyBB->begin()),
9308 ExitBB);
9309 if (!AfterIP)
9310 return AfterIP.takeError();
9311 Builder.SetInsertPoint(ExitBB);
9312 // The body callback builds the body with its own IRBuilder and cannot reach
9313 // this one directly. But a body holding another OpenMP construct, a nested
9314 // parallel say, calls OpenMPIRBuilder::createParallel, and that can leave
9315 // this Builder pointing at the wrong debug location, or at none at all. The
9316 // epilogue below belongs to the target construct rather than to whatever the
9317 // body emitted last, so re-establish the location the prologue was emitted
9318 // with.
9319 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9320
9321 // Insert target deinit call in the device compilation pass.
9322 if (OMPBuilder.Config.isTargetDevice())
9323 OMPBuilder.createTargetDeinit(Builder);
9324
9325 // Insert return instruction.
9326 Builder.CreateRetVoid();
9327
9328 // New Alloca IP at entry point of created device function.
9329 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9330 auto AllocaIP = Builder.saveIP();
9331
9332 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());
9333
9334 // Do not include the artificial dyn_ptr argument.
9335 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9336
9338
9339 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9340 // Things like GEP's can come in the form of Constants. Constants and
9341 // ConstantExpr's do not have access to the knowledge of what they're
9342 // contained in, so we must dig a little to find an instruction so we
9343 // can tell if they're used inside of the function we're outlining. We
9344 // also replace the original constant expression with a new instruction
9345 // equivalent; an instruction as it allows easy modification in the
9346 // following loop, as we can now know the constant (instruction) is
9347 // owned by our target function and replaceUsesOfWith can now be invoked
9348 // on it (cannot do this with constants it seems). A brand new one also
9349 // allows us to be cautious as it is perhaps possible the old expression
9350 // was used inside of the function but exists and is used externally
9351 // (unlikely by the nature of a Constant, but still).
9352 // NOTE: We cannot remove dead constants that have been rewritten to
9353 // instructions at this stage, we run the risk of breaking later lowering
9354 // by doing so as we could still be in the process of lowering the module
9355 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9356 // constants we have created rewritten versions of.
9357 if (auto *Const = dyn_cast<Constant>(Input))
9358 convertUsersOfConstantsToInstructions(Const, Func, false);
9359
9360 // Collect users before iterating over them to avoid invalidating the
9361 // iteration in case a user uses Input more than once (e.g. a call
9362 // instruction).
9363 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9364 // Collect all the instructions
9366 if (auto *Instr = dyn_cast<Instruction>(User))
9367 if (Instr->getFunction() == Func)
9368 Instr->replaceUsesOfWith(Input, InputCopy);
9369 };
9370
9371 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9372
9373 // Rewrite uses of input valus to parameters.
9374 for (auto InArg : zip(Inputs, ArgRange)) {
9375 Value *Input = std::get<0>(InArg);
9376 Argument &Arg = std::get<1>(InArg);
9377 Value *InputCopy = nullptr;
9378
9379 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9380 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(),
9381 OpenMPIRBuilder::InsertPointTy(ExitBB, ExitBB->begin()));
9382 if (!AfterIP)
9383 return AfterIP.takeError();
9384 Builder.restoreIP(*AfterIP);
9385 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9386
9387 // In certain cases a Global may be set up for replacement, however, this
9388 // Global may be used in multiple arguments to the kernel, just segmented
9389 // apart, for example, if we have a global array, that is sectioned into
9390 // multiple mappings (technically not legal in OpenMP, but there is a case
9391 // in Fortran for Common Blocks where this is neccesary), we will end up
9392 // with GEP's into this array inside the kernel, that refer to the Global
9393 // but are technically separate arguments to the kernel for all intents and
9394 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9395 // index, it will fold into an referal to the Global, if we then encounter
9396 // this folded GEP during replacement all of the references to the
9397 // Global in the kernel will be replaced with the argument we have generated
9398 // that corresponds to it, including any other GEP's that refer to the
9399 // Global that may be other arguments. This will invalidate all of the other
9400 // preceding mapped arguments that refer to the same global that may be
9401 // separate segments. To prevent this, we defer global processing until all
9402 // other processing has been performed.
9405 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9406 continue;
9407 }
9408
9410 continue;
9411
9412 ReplaceValue(Input, InputCopy, Func);
9413 }
9414
9415 // Replace all of our deferred Input values, currently just Globals.
9416 for (auto Deferred : DeferredReplacement)
9417 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9418
9419 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9420 ValueReplacementMap);
9421 return Func;
9422}
9423/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9424/// of pointers containing shared data between the parent task and the created
9425/// task.
9427 IRBuilderBase &Builder,
9428 Value *TaskWithPrivates,
9429 Type *TaskWithPrivatesTy) {
9430
9431 Type *TaskTy = OMPIRBuilder.Task;
9432 LLVMContext &Ctx = Builder.getContext();
9433 Value *TaskT =
9434 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9435 Value *Shareds = TaskT;
9436 // TaskWithPrivatesTy can be one of the following
9437 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9438 // %struct.privates }
9439 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9440 //
9441 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9442 // its first member has to be the task descriptor. TaskTy is the type of the
9443 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9444 // first member of TaskT, gives us the pointer to shared data.
9445 if (TaskWithPrivatesTy != TaskTy)
9446 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9447 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9448}
9449/// Create an entry point for a target task with the following.
9450/// It'll have the following signature
9451/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9452/// This function is called from emitTargetTask once the
9453/// code to launch the target kernel has been outlined already.
9454/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9455/// into the task structure so that the deferred target task can access this
9456/// data even after the stack frame of the generating task has been rolled
9457/// back. Offloading arrays contain base pointers, pointers, sizes etc
9458/// of the data that the target kernel will access. These in effect are the
9459/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9461 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9462 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9463 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9464
9465 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9466 // This is because PrivatesTy is the type of the structure in which
9467 // we pass the offloading arrays to the deferred target task.
9468 assert((!NumOffloadingArrays || PrivatesTy) &&
9469 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9470 "to privatize");
9471
9472 Module &M = OMPBuilder.M;
9473 // KernelLaunchFunction is the target launch function, i.e.
9474 // the function that sets up kernel arguments and calls
9475 // __tgt_target_kernel to launch the kernel on the device.
9476 //
9477 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9478
9479 // StaleCI is the CallInst which is the call to the outlined
9480 // target kernel launch function. If there are local live-in values
9481 // that the outlined function uses then these are aggregated into a structure
9482 // which is passed as the second argument. If there are no local live-in
9483 // values or if all values used by the outlined kernel are global variables,
9484 // then there's only one argument, the threadID. So, StaleCI can be
9485 //
9486 // %structArg = alloca { ptr, ptr }, align 8
9487 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9488 // store ptr %20, ptr %gep_, align 8
9489 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9490 // store ptr %21, ptr %gep_8, align 8
9491 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9492 //
9493 // OR
9494 //
9495 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9497 StaleCI->getIterator());
9498
9499 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9500
9501 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9502 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9503 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9504
9505 auto ProxyFnTy =
9506 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9507 /* isVarArg */ false);
9508 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9509 ".omp_target_task_proxy_func", M);
9510 Value *ThreadId = ProxyFn->getArg(0);
9511 Value *TaskWithPrivates = ProxyFn->getArg(1);
9512 ThreadId->setName("thread.id");
9513 TaskWithPrivates->setName("task");
9514
9515 bool HasShareds = SharedArgsOperandNo > 0;
9516 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9517 IRBuilder<>::InsertPointGuard IPG(Builder);
9518 BasicBlock *EntryBB =
9519 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9520 Builder.SetInsertPoint(EntryBB);
9521 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9522
9523 SmallVector<Value *> KernelLaunchArgs;
9524 KernelLaunchArgs.reserve(StaleCI->arg_size());
9525 KernelLaunchArgs.push_back(ThreadId);
9526
9527 if (HasOffloadingArrays) {
9528 assert(TaskTy != TaskWithPrivatesTy &&
9529 "If there are offloading arrays to pass to the target"
9530 "TaskTy cannot be the same as TaskWithPrivatesTy");
9531 (void)TaskTy;
9532 Value *Privates =
9533 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9534 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9535 KernelLaunchArgs.push_back(
9536 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9537 }
9538
9539 if (HasShareds) {
9540 auto *ArgStructAlloca =
9541 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9542 assert(ArgStructAlloca &&
9543 "Unable to find the alloca instruction corresponding to arguments "
9544 "for extracted function");
9545 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9546 std::optional<TypeSize> ArgAllocSize =
9547 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9548 assert(ArgStructType && ArgAllocSize &&
9549 "Unable to determine size of arguments for extracted function");
9550 uint64_t StructSize = ArgAllocSize->getFixedValue();
9551
9552 AllocaInst *NewArgStructAlloca =
9553 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9554
9555 Value *SharedsSize = Builder.getInt64(StructSize);
9556
9558 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9559
9560 Builder.CreateMemCpy(
9561 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9562 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9563 KernelLaunchArgs.push_back(NewArgStructAlloca);
9564 }
9565 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9566 Builder.CreateRetVoid();
9567 return ProxyFn;
9568}
9570
9571 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9572 return GEP->getSourceElementType();
9573 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9574 return Alloca->getAllocatedType();
9575
9576 llvm_unreachable("Unhandled Instruction type");
9577 return nullptr;
9578}
9579// This function returns a struct that has at most two members.
9580// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9581// descriptor. The second member, if needed, is a struct containing arrays
9582// that need to be passed to the offloaded target kernel. For example,
9583// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9584// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9585// respectively, then the types created by this function are
9586//
9587// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9588// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9589// %struct.privates }
9590// %struct.task_with_privates is returned by this function.
9591// If there aren't any offloading arrays to pass to the target kernel,
9592// %struct.kmp_task_ompbuilder_t is returned.
9593static StructType *
9595 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9596
9597 if (OffloadingArraysToPrivatize.empty())
9598 return OMPIRBuilder.Task;
9599
9600 SmallVector<Type *, 4> StructFieldTypes;
9601 for (Value *V : OffloadingArraysToPrivatize) {
9602 assert(V->getType()->isPointerTy() &&
9603 "Expected pointer to array to privatize. Got a non-pointer value "
9604 "instead");
9605 Type *ArrayTy = getOffloadingArrayType(V);
9606 assert(ArrayTy && "ArrayType cannot be nullptr");
9607 StructFieldTypes.push_back(ArrayTy);
9608 }
9609 StructType *PrivatesStructTy =
9610 StructType::create(StructFieldTypes, "struct.privates");
9611 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9612 "struct.task_with_privates");
9613}
9615 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9616 TargetRegionEntryInfo &EntryInfo,
9618 Function *&OutlinedFn, Constant *&OutlinedFnID,
9622 DebugLoc OutlinedFnLoc) {
9623
9624 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9625 [&](StringRef EntryFnName) {
9626 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9627 EntryFnName, Inputs, CBFunc,
9628 ArgAccessorFuncCB, OutlinedFnLoc);
9629 };
9630
9631 return OMPBuilder.emitTargetRegionFunction(
9632 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9633 OutlinedFnID);
9634}
9635
9637 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9639 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9640 bool HasNoWait) {
9641
9642 // The following explains the code-gen scenario for the `target` directive. A
9643 // similar scneario is followed for other device-related directives (e.g.
9644 // `target enter data`) but in similar fashion since we only need to emit task
9645 // that encapsulates the proper runtime call.
9646 //
9647 // When we arrive at this function, the target region itself has been
9648 // outlined into the function OutlinedFn.
9649 // So at ths point, for
9650 // --------------------------------------------------------------
9651 // void user_code_that_offloads(...) {
9652 // omp target depend(..) map(from:a) map(to:b) private(i)
9653 // do i = 1, 10
9654 // a(i) = b(i) + n
9655 // }
9656 //
9657 // --------------------------------------------------------------
9658 //
9659 // we have
9660 //
9661 // --------------------------------------------------------------
9662 //
9663 // void user_code_that_offloads(...) {
9664 // %.offload_baseptrs = alloca [2 x ptr], align 8
9665 // %.offload_ptrs = alloca [2 x ptr], align 8
9666 // %.offload_mappers = alloca [2 x ptr], align 8
9667 // ;; target region has been outlined and now we need to
9668 // ;; offload to it via a target task.
9669 // }
9670 // void outlined_device_function(ptr a, ptr b, ptr n) {
9671 // n = *n_ptr;
9672 // do i = 1, 10
9673 // a(i) = b(i) + n
9674 // }
9675 //
9676 // We have to now do the following
9677 // (i) Make an offloading call to outlined_device_function using the OpenMP
9678 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9679 // emitted by emitKernelLaunch
9680 // (ii) Create a task entry point function that calls kernel_launch_function
9681 // and is the entry point for the target task. See
9682 // '@.omp_target_task_proxy_func in the pseudocode below.
9683 // (iii) Create a task with the task entry point created in (ii)
9684 //
9685 // That is we create the following
9686 // struct task_with_privates {
9687 // struct kmp_task_ompbuilder_t task_struct;
9688 // struct privates {
9689 // [2 x ptr] ; baseptrs
9690 // [2 x ptr] ; ptrs
9691 // [2 x i64] ; sizes
9692 // }
9693 // }
9694 // void user_code_that_offloads(...) {
9695 // %.offload_baseptrs = alloca [2 x ptr], align 8
9696 // %.offload_ptrs = alloca [2 x ptr], align 8
9697 // %.offload_sizes = alloca [2 x i64], align 8
9698 //
9699 // %structArg = alloca { ptr, ptr, ptr }, align 8
9700 // %strucArg[0] = a
9701 // %strucArg[1] = b
9702 // %strucArg[2] = &n
9703 //
9704 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9705 // sizeof(kmp_task_ompbuilder_t),
9706 // sizeof(structArg),
9707 // @.omp_target_task_proxy_func,
9708 // ...)
9709 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9710 // sizeof(structArg))
9711 // memcpy(target_task_with_privates->privates->baseptrs,
9712 // offload_baseptrs, sizeof(offload_baseptrs)
9713 // memcpy(target_task_with_privates->privates->ptrs,
9714 // offload_ptrs, sizeof(offload_ptrs)
9715 // memcpy(target_task_with_privates->privates->sizes,
9716 // offload_sizes, sizeof(offload_sizes)
9717 // dependencies_array = ...
9718 // ;; if nowait not present
9719 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9720 // call @__kmpc_omp_task_begin_if0(...)
9721 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9722 // %target_task_with_privates)
9723 // call @__kmpc_omp_task_complete_if0(...)
9724 // }
9725 //
9726 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9727 // ptr %task) {
9728 // %structArg = alloca {ptr, ptr, ptr}
9729 // %task_ptr = getelementptr(%task, 0, 0)
9730 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9731 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9732 //
9733 // %offloading_arrays = getelementptr(%task, 0, 1)
9734 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9735 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9736 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9737 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9738 // %offload_sizes, %structArg)
9739 // }
9740 //
9741 // We need the proxy function because the signature of the task entry point
9742 // expected by kmpc_omp_task is always the same and will be different from
9743 // that of the kernel_launch function.
9744 //
9745 // kernel_launch_function is generated by emitKernelLaunch and has the
9746 // always_inline attribute. For this example, it'll look like so:
9747 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9748 // %offload_sizes, %structArg) alwaysinline {
9749 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9750 // ; load aggregated data from %structArg
9751 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9752 // ; offload_sizes
9753 // call i32 @__tgt_target_kernel(...,
9754 // outlined_device_function,
9755 // ptr %kernel_args)
9756 // }
9757 // void outlined_device_function(ptr a, ptr b, ptr n) {
9758 // n = *n_ptr;
9759 // do i = 1, 10
9760 // a(i) = b(i) + n
9761 // }
9762 //
9763 BasicBlock *TargetTaskBodyBB =
9764 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9765 BasicBlock *TargetTaskAllocaBB =
9766 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9767
9768 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB,
9769 TargetTaskAllocaBB->begin());
9770 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB, TargetTaskBodyBB->begin());
9771
9772 auto OI = std::make_unique<OutlineInfo>();
9773 OI->EntryBB = TargetTaskAllocaBB;
9774 OI->OuterAllocBB = AllocaIP.getBlock();
9775
9776 // Add the thread ID argument.
9778 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9779 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9780
9781 // Generate the task body which will subsequently be outlined.
9782 Builder.restoreIP(TargetTaskBodyIP);
9783 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9784 return Err;
9785
9786 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9787 // it is given. These blocks are enumerated by
9788 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9789 // to be outside the region. In other words, OI.ExitBlock is expected to be
9790 // the start of the region after the outlining. We used to set OI.ExitBlock
9791 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9792 // except when the task body is a single basic block. In that case,
9793 // OI.ExitBlock is set to the single task body block and will get left out of
9794 // the outlining process. So, simply create a new empty block to which we
9795 // uncoditionally branch from where TaskBodyCB left off
9796 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9797 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9798 /*IsFinished=*/true);
9799
9800 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9801 bool NeedsTargetTask = HasNoWait && DeviceID;
9802 if (NeedsTargetTask) {
9803 for (auto *V :
9804 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9805 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9806 RTArgs.SizesArray}) {
9808 OffloadingArraysToPrivatize.push_back(V);
9809 OI->ExcludeArgsFromAggregate.push_back(V);
9810 }
9811 }
9812 }
9813 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9814 DeviceID, OffloadingArraysToPrivatize](
9815 Function &OutlinedFn) mutable {
9816 assert(OutlinedFn.hasOneUse() &&
9817 "there must be a single user for the outlined function");
9818
9819 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9820
9821 // The first argument of StaleCI is always the thread id.
9822 // The next few arguments are the pointers to offloading arrays
9823 // if any. (see OffloadingArraysToPrivatize)
9824 // Finally, all other local values that are live-in into the outlined region
9825 // end up in a structure whose pointer is passed as the last argument. This
9826 // piece of data is passed in the "shared" field of the task structure. So,
9827 // we know we have to pass shareds to the task if the number of arguments is
9828 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9829 // thread id. Further, for safety, we assert that the number of arguments of
9830 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9831 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9832 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9833 assert((!HasShareds ||
9834 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9835 "Wrong number of arguments for StaleCI when shareds are present");
9836 int SharedArgOperandNo =
9837 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9838
9839 StructType *TaskWithPrivatesTy =
9840 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9841 StructType *PrivatesTy = nullptr;
9842
9843 if (!OffloadingArraysToPrivatize.empty())
9844 PrivatesTy =
9845 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9846
9848 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9849 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9850
9851 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9852 << "\n");
9853
9854 Builder.SetInsertPoint(StaleCI);
9855
9856 // Gather the arguments for emitting the runtime call.
9857 uint32_t SrcLocStrSize;
9858 Constant *SrcLocStr =
9860 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9861
9862 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9863 //
9864 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9865 // the DeviceID to the deferred task and also since
9866 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9867 Function *TaskAllocFn =
9868 !NeedsTargetTask
9869 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9871 OMPRTL___kmpc_omp_target_task_alloc);
9872
9873 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9874 // call.
9875 Value *ThreadID = getOrCreateThreadID(Ident);
9876
9877 // Argument - `sizeof_kmp_task_t` (TaskSize)
9878 // Tasksize refers to the size in bytes of kmp_task_t data structure
9879 // plus any other data to be passed to the target task, if any, which
9880 // is packed into a struct. kmp_task_t and the struct so created are
9881 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9882 Value *TaskSize = Builder.getInt64(
9883 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9884
9885 // Argument - `sizeof_shareds` (SharedsSize)
9886 // SharedsSize refers to the shareds array size in the kmp_task_t data
9887 // structure.
9888 Value *SharedsSize = Builder.getInt64(0);
9889 if (HasShareds) {
9890 auto *ArgStructAlloca =
9891 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9892 assert(ArgStructAlloca &&
9893 "Unable to find the alloca instruction corresponding to arguments "
9894 "for extracted function");
9895 std::optional<TypeSize> ArgAllocSize =
9896 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9897 assert(ArgAllocSize &&
9898 "Unable to determine size of arguments for extracted function");
9899 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9900 }
9901
9902 // Argument - `flags`
9903 // Task is tied iff (Flags & 1) == 1.
9904 // Task is untied iff (Flags & 1) == 0.
9905 // Task is final iff (Flags & 2) == 2.
9906 // Task is not final iff (Flags & 2) == 0.
9907 // A target task is not final and is untied.
9908 Value *Flags = Builder.getInt32(0);
9909
9910 // Emit the @__kmpc_omp_task_alloc runtime call
9911 // The runtime call returns a pointer to an area where the task captured
9912 // variables must be copied before the task is run (TaskData)
9913 CallInst *TaskData = nullptr;
9914
9915 SmallVector<llvm::Value *> TaskAllocArgs = {
9916 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
9917 /*flags=*/Flags,
9918 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
9919 /*task_func=*/ProxyFn};
9920
9921 if (NeedsTargetTask) {
9922 assert(DeviceID && "Expected non-empty device ID.");
9923 TaskAllocArgs.push_back(DeviceID);
9924 }
9925
9926 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
9927
9928 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
9929 if (HasShareds) {
9930 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
9932 *this, Builder, TaskData, TaskWithPrivatesTy);
9933 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
9934 SharedsSize);
9935 }
9936 if (!OffloadingArraysToPrivatize.empty()) {
9937 Value *Privates =
9938 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
9939 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
9940 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
9941 [[maybe_unused]] Type *ArrayType =
9942 getOffloadingArrayType(PtrToPrivatize);
9943 assert(ArrayType && "ArrayType cannot be nullptr");
9944
9945 Type *ElementType = PrivatesTy->getElementType(i);
9946 assert(ElementType == ArrayType &&
9947 "ElementType should match ArrayType");
9948 (void)ArrayType;
9949
9950 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
9951 Builder.CreateMemCpy(
9952 Dst, Alignment, PtrToPrivatize, Alignment,
9953 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
9954 }
9955 }
9956
9957 Value *DepArray = nullptr;
9958 Value *NumDeps = nullptr;
9959 if (Dependencies.DepArray) {
9960 DepArray = Dependencies.DepArray;
9961 NumDeps = Dependencies.NumDeps;
9962 } else if (!Dependencies.Deps.empty()) {
9963 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
9964 NumDeps = Builder.getInt32(Dependencies.Deps.size());
9965 }
9966
9967 // ---------------------------------------------------------------
9968 // V5.2 13.8 target construct
9969 // If the nowait clause is present, execution of the target task
9970 // may be deferred. If the nowait clause is not present, the target task is
9971 // an included task.
9972 // ---------------------------------------------------------------
9973 // The above means that the lack of a nowait on the target construct
9974 // translates to '#pragma omp task if(0)'
9975 if (!NeedsTargetTask) {
9976 if (DepArray) {
9977 Function *TaskWaitFn =
9978 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
9980 TaskWaitFn,
9981 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
9982 /*ndeps=*/NumDeps,
9983 /*dep_list=*/DepArray,
9984 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
9985 /*noalias_dep_list=*/
9987 }
9988 // Included task.
9989 Function *TaskBeginFn =
9990 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
9991 Function *TaskCompleteFn =
9992 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
9993 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
9994 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
9995 CI->setDebugLoc(StaleCI->getDebugLoc());
9996 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
9997 } else if (DepArray) {
9998 // HasNoWait - meaning the task may be deferred. Call
9999 // __kmpc_omp_task_with_deps if there are dependencies,
10000 // else call __kmpc_omp_task
10001 Function *TaskFn =
10002 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
10004 TaskFn,
10005 {Ident, ThreadID, TaskData, NumDeps, DepArray,
10006 ConstantInt::get(Builder.getInt32Ty(), 0),
10008 } else {
10009 // Emit the @__kmpc_omp_task runtime call to spawn the task
10010 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
10011 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
10012 }
10013
10014 Builder.ClearInsertionPoint();
10015 StaleCI->eraseFromParent();
10016 for (Instruction *I : llvm::reverse(ToBeDeleted))
10017 I->eraseFromParent();
10018 };
10019 addOutlineInfo(std::move(OI));
10020
10021 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
10022 << *(Builder.GetInsertBlock()) << "\n");
10023 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
10024 << *(Builder.GetInsertBlock()->getParent()->getParent())
10025 << "\n");
10026 return Builder.saveIP();
10027}
10028
10030 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10031 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10032 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10033 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10034 if (Error Err =
10035 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10036 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10037 return Err;
10038 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10039 return Error::success();
10040}
10041
10042static void emitTargetCall(
10043 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
10048 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10052 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10053 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10054 // Generate a function call to the host fallback implementation of the target
10055 // region. This is called by the host when no offload entry was generated for
10056 // the target region and when the offloading call fails at runtime.
10057 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10059 Builder.restoreIP(IP);
10060 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10061 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10062 FallbackArgs.push_back(
10063 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10064 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10065 return Builder.saveIP();
10066 };
10067
10068 bool HasDependencies = !Dependencies.empty();
10069 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10070
10072
10073 auto TaskBodyCB =
10074 [&](Value *DeviceID, Value *RTLoc,
10075 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10076 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10077 // produce any.
10079 // emitKernelLaunch makes the necessary runtime call to offload the
10080 // kernel. We then outline all that code into a separate function
10081 // ('kernel_launch_function' in the pseudo code above). This function is
10082 // then called by the target task proxy function (see
10083 // '@.omp_target_task_proxy_func' in the pseudo code above)
10084 // "@.omp_target_task_proxy_func' is generated by
10085 // emitTargetTaskProxyFunction.
10086 if (OutlinedFnID && DeviceID)
10087 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10088 EmitTargetCallFallbackCB, KArgs,
10089 DeviceID, RTLoc, TargetTaskAllocaIP);
10090
10091 // We only need to do the outlining if `DeviceID` is set to avoid calling
10092 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10093 // generating the `else` branch of an `if` clause.
10094 //
10095 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10096 // In this case, we execute the host implementation directly.
10097 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10098 }());
10099
10100 OMPBuilder.Builder.restoreIP(AfterIP);
10101 return Error::success();
10102 };
10103
10104 auto &&EmitTargetCallElse =
10105 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10107 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10108 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10109 // produce any.
10111 if (RequiresOuterTargetTask) {
10112 // Arguments that are intended to be directly forwarded to an
10113 // emitKernelLaunch call are pased as nullptr, since
10114 // OutlinedFnID=nullptr results in that call not being done.
10116 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10117 /*RTLoc=*/nullptr, AllocaIP,
10118 Dependencies, EmptyRTArgs, HasNoWait);
10119 }
10120 return EmitTargetCallFallbackCB(Builder.saveIP());
10121 }());
10122
10123 Builder.restoreIP(AfterIP);
10124 return Error::success();
10125 };
10126
10127 auto &&EmitTargetCallThen =
10128 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10130 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10131 Info.HasNoWait = HasNoWait;
10132 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10133
10135 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10136 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10137 /*IsNonContiguous=*/true,
10138 /*ForEndCall=*/false))
10139 return Err;
10140
10141 SmallVector<Value *, 3> NumTeamsC;
10142 for (auto [DefaultVal, RuntimeVal] :
10143 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10144 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10145 : Builder.getInt32(DefaultVal));
10146
10147 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10148 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10149 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10150 if (Clause)
10151 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10152 /*isSigned=*/false);
10153 return Clause;
10154 };
10155 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10156 if (Clause)
10157 Result =
10158 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10159 Result, Clause)
10160 : Clause;
10161 };
10162
10163 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10164 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10165 SmallVector<Value *, 3> NumThreadsC;
10166 Value *MaxThreadsClause =
10167 RuntimeAttrs.TeamsThreadLimit.size() == 1
10168 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads.front())
10169 : nullptr;
10170
10171 for (auto [TeamsVal, TargetVal] : zip_equal(
10172 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10173 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10174 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10175
10176 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10177 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10178
10179 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10180 }
10181
10182 unsigned NumTargetItems = Info.NumberOfPtrs;
10183 uint32_t SrcLocStrSize;
10184 Constant *SrcLocStr = OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10185 Value *RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10186 llvm::omp::IdentFlag(0), 0);
10187
10188 Value *TripCount = RuntimeAttrs.LoopTripCount
10189 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10190 Builder.getInt64Ty(),
10191 /*isSigned=*/false)
10192 : Builder.getInt64(0);
10193
10194 // Request zero groupprivate bytes by default.
10195 if (!DynCGroupMem)
10196 DynCGroupMem = Builder.getInt32(0);
10197
10199 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10200 HasNoWait, /*StrictBlocks=*/false, /*StrictThreads=*/false,
10201 DynCGroupMemFallback);
10202
10203 // Assume no error was returned because TaskBodyCB and
10204 // EmitTargetCallFallbackCB don't produce any.
10206 // The presence of certain clauses on the target directive require the
10207 // explicit generation of the target task.
10208 if (RequiresOuterTargetTask)
10209 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10210 RTLoc, AllocaIP, Dependencies,
10211 KArgs.RTArgs, Info.HasNoWait);
10212
10213 return OMPBuilder.emitKernelLaunch(
10214 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10215 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10216 }());
10217
10218 Builder.restoreIP(AfterIP);
10219 return Error::success();
10220 };
10221
10222 // If we don't have an ID for the target region, it means an offload entry
10223 // wasn't created. In this case we just run the host fallback directly and
10224 // ignore any potential 'if' clauses.
10225 if (!OutlinedFnID) {
10226 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10227 return;
10228 }
10229
10230 // If there's no 'if' clause, only generate the kernel launch code path.
10231 if (!IfCond) {
10232 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10233 return;
10234 }
10235
10236 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10237 EmitTargetCallElse, AllocaIP));
10238}
10239
10241 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10242 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10243 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10244 const TargetKernelDefaultAttrs &DefaultAttrs,
10245 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10246 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10249 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10250 bool HasNowait, Value *DynCGroupMem,
10251 OMPDynGroupprivateFallbackType DynCGroupMemFallback,
10252 DebugLoc OutlinedFnLoc) {
10253
10254 if (!updateToLocation(Loc))
10255 return InsertPointTy();
10256
10257 Builder.restoreIP(CodeGenIP);
10258
10259 Function *OutlinedFn;
10260 Constant *OutlinedFnID = nullptr;
10261 // The target region is outlined into its own function. The LLVM IR for
10262 // the target region itself is generated using the callbacks CBFunc
10263 // and ArgAccessorFuncCB
10265 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10266 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB, OutlinedFnLoc))
10267 return Err;
10268
10269 // If we are not on the target device, then we need to generate code
10270 // to make a remote call (offload) to the previously outlined function
10271 // that represents the target region. Do that now.
10272 if (!Config.isTargetDevice())
10273 emitTargetCall(*this, Builder, AllocaIP, DeallocBlocks, Info, DefaultAttrs,
10274 RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID, Inputs,
10275 GenMapInfoCB, CustomMapperCB, Dependencies, HasNowait,
10276 DynCGroupMem, DynCGroupMemFallback);
10277 return Builder.saveIP();
10278}
10279
10280std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10281 StringRef FirstSeparator,
10282 StringRef Separator) {
10283 SmallString<128> Buffer;
10284 llvm::raw_svector_ostream OS(Buffer);
10285 StringRef Sep = FirstSeparator;
10286 for (StringRef Part : Parts) {
10287 OS << Sep << Part;
10288 Sep = Separator;
10289 }
10290 return OS.str().str();
10291}
10292
10293std::string
10295 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10296 Config.separator());
10297}
10298
10300 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10301 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10302 if (Elem.second) {
10303 assert(Elem.second->getValueType() == Ty &&
10304 "OMP internal variable has different type than requested");
10305 } else {
10306 // TODO: investigate the appropriate linkage type used for the global
10307 // variable for possibly changing that to internal or private, or maybe
10308 // create different versions of the function for different OMP internal
10309 // variables.
10310 const DataLayout &DL = M.getDataLayout();
10311 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10312 // default global AS is 1.
10313 // See double-target-call-with-declare-target.f90 and
10314 // declare-target-vars-in-target-region.f90 libomptarget
10315 // tests.
10316 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10317 : M.getTargetTriple().isAMDGPU()
10318 ? 0
10319 : DL.getDefaultGlobalsAddressSpace();
10320 auto Linkage = this->M.getTargetTriple().isWasm()
10323 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10324 Constant::getNullValue(Ty), Elem.first(),
10325 /*InsertBefore=*/nullptr,
10326 GlobalValue::NotThreadLocal, AddressSpaceVal);
10327 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10328 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10329 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10330 Elem.second = GV;
10331 }
10332
10333 return Elem.second;
10334}
10335
10336Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10337 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10338 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10339 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10340}
10341
10343 LLVMContext &Ctx = Builder.getContext();
10344 Value *Null =
10345 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10346 Value *SizeGep =
10347 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10348 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10349 return SizePtrToInt;
10350}
10351
10354 std::string VarName) {
10355 llvm::Constant *MaptypesArrayInit =
10356 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10357 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10358 M, MaptypesArrayInit->getType(),
10359 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10360 VarName);
10361 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10362 return MaptypesArrayGlobal;
10363}
10364
10366 InsertPointTy AllocaIP,
10367 unsigned NumOperands,
10368 struct MapperAllocas &MapperAllocas) {
10369 if (!updateToLocation(Loc))
10370 return;
10371
10372 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10373 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10374 Builder.restoreIP(AllocaIP);
10375 AllocaInst *ArgsBase = Builder.CreateAlloca(
10376 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10377 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10378 ".offload_ptrs");
10379 AllocaInst *ArgSizes = Builder.CreateAlloca(
10380 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10382 MapperAllocas.ArgsBase = ArgsBase;
10383 MapperAllocas.Args = Args;
10384 MapperAllocas.ArgSizes = ArgSizes;
10385}
10386
10388 Function *MapperFunc, Value *SrcLocInfo,
10389 Value *MaptypesArg, Value *MapnamesArg,
10391 int64_t DeviceID, unsigned NumOperands) {
10392 if (!updateToLocation(Loc))
10393 return;
10394
10395 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10396 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10397 Value *ArgsBaseGEP =
10398 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10399 {Builder.getInt32(0), Builder.getInt32(0)});
10400 Value *ArgsGEP =
10401 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10402 {Builder.getInt32(0), Builder.getInt32(0)});
10403 Value *ArgSizesGEP =
10404 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10405 {Builder.getInt32(0), Builder.getInt32(0)});
10406 Value *NullPtr =
10407 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10408 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10409 Builder.getInt32(NumOperands),
10410 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10411 MaptypesArg, MapnamesArg, NullPtr});
10412}
10413
10415 TargetDataRTArgs &RTArgs,
10416 TargetDataInfo &Info,
10417 bool ForEndCall) {
10418 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10419 "expected region end call to runtime only when end call is separate");
10420 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10421 auto VoidPtrTy = UnqualPtrTy;
10422 auto VoidPtrPtrTy = UnqualPtrTy;
10423 auto Int64Ty = Type::getInt64Ty(M.getContext());
10424 auto Int64PtrTy = UnqualPtrTy;
10425
10426 if (!Info.NumberOfPtrs) {
10427 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10428 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10429 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10430 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10431 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10432 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10433 return;
10434 }
10435
10436 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10437 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10438 Info.RTArgs.BasePointersArray,
10439 /*Idx0=*/0, /*Idx1=*/0);
10440 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10441 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10442 /*Idx0=*/0,
10443 /*Idx1=*/0);
10444 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10445 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10446 /*Idx0=*/0, /*Idx1=*/0);
10447 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10448 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10449 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10450 : Info.RTArgs.MapTypesArray,
10451 /*Idx0=*/0,
10452 /*Idx1=*/0);
10453
10454 // Only emit the mapper information arrays if debug information is
10455 // requested.
10456 if (!Info.EmitDebug)
10457 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10458 else
10459 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10460 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10461 /*Idx0=*/0,
10462 /*Idx1=*/0);
10463 // If there is no user-defined mapper, set the mapper array to nullptr to
10464 // avoid an unnecessary data privatization
10465 if (!Info.HasMapper)
10466 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10467 else
10468 RTArgs.MappersArray =
10469 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10470}
10471
10473 InsertPointTy CodeGenIP,
10474 MapInfosTy &CombinedInfo,
10475 TargetDataInfo &Info) {
10477 CombinedInfo.NonContigInfo;
10478
10479 // Build an array of struct descriptor_dim and then assign it to
10480 // offload_args.
10481 //
10482 // struct descriptor_dim {
10483 // uint64_t offset;
10484 // uint64_t count;
10485 // uint64_t stride
10486 // };
10487 Type *Int64Ty = Builder.getInt64Ty();
10489 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10490 "struct.descriptor_dim");
10491
10492 enum { OffsetFD = 0, CountFD, StrideFD };
10493 // We need two index variable here since the size of "Dims" is the same as
10494 // the size of Components, however, the size of offset, count, and stride is
10495 // equal to the size of base declaration that is non-contiguous.
10496 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10497 // Skip emitting ir if dimension size is 1 since it cannot be
10498 // non-contiguous.
10499 if (NonContigInfo.Dims[I] == 1)
10500 continue;
10501 Builder.restoreIP(AllocaIP);
10502 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10503 AllocaInst *DimsAddr =
10504 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10505 Builder.restoreIP(CodeGenIP);
10506 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10507 unsigned RevIdx = EE - II - 1;
10508 Value *DimsLVal = Builder.CreateInBoundsGEP(
10509 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10510 // Offset
10511 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10512 Builder.CreateAlignedStore(
10513 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10514 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10515 // Count
10516 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10517 Builder.CreateAlignedStore(
10518 NonContigInfo.Counts[L][RevIdx], CountLVal,
10519 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10520 // Stride
10521 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10522 Builder.CreateAlignedStore(
10523 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10524 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10525 }
10526 // args[I] = &dims
10527 Builder.restoreIP(CodeGenIP);
10528 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10529 DimsAddr, Builder.getPtrTy());
10530 Value *P = Builder.CreateConstInBoundsGEP2_32(
10531 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10532 Info.RTArgs.PointersArray, 0, I);
10533 Builder.CreateAlignedStore(
10534 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10535 ++L;
10536 }
10537}
10538
10539void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10540 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10541 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10542 BasicBlock *ExitBB, bool IsInit) {
10543 StringRef Prefix = IsInit ? ".init" : ".del";
10544
10545 // Evaluate if this is an array section.
10547 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10548 Value *IsArray =
10549 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10550 Value *DeleteBit = Builder.CreateAnd(
10551 MapType,
10552 Builder.getInt64(
10553 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10554 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10555 Value *DeleteCond;
10556 Value *Cond;
10557 if (IsInit) {
10558 // base != begin?
10559 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10560 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10561 DeleteCond = Builder.CreateIsNull(
10562 DeleteBit,
10563 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10564 } else {
10565 Cond = IsArray;
10566 DeleteCond = Builder.CreateIsNotNull(
10567 DeleteBit,
10568 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10569 }
10570 Cond = Builder.CreateAnd(Cond, DeleteCond);
10571 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10572
10573 emitBlock(BodyBB, MapperFn);
10574 // Get the array size by multiplying element size and element number (i.e., \p
10575 // Size).
10576 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10577 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10578 // memory allocation/deletion purpose only.
10579 Value *MapTypeArg = Builder.CreateAnd(
10580 MapType,
10581 Builder.getInt64(
10582 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10583 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10584 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10585 MapTypeArg = Builder.CreateOr(
10586 MapTypeArg,
10587 Builder.getInt64(
10588 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10589 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10590
10591 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10592 // data structure.
10593 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10594 ArraySize, MapTypeArg, MapName};
10596 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10597 OffloadingArgs);
10598}
10599
10602 llvm::Value *BeginArg)>
10603 GenMapInfoCB,
10604 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10605 bool PreserveMemberOfFlags, bool PropagatePresentToPointee) {
10606 SmallVector<Type *> Params;
10607 Params.emplace_back(Builder.getPtrTy());
10608 Params.emplace_back(Builder.getPtrTy());
10609 Params.emplace_back(Builder.getPtrTy());
10610 Params.emplace_back(Builder.getInt64Ty());
10611 Params.emplace_back(Builder.getInt64Ty());
10612 Params.emplace_back(Builder.getPtrTy());
10613
10614 auto *FnTy =
10615 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10616
10617 SmallString<64> TyStr;
10618 raw_svector_ostream Out(TyStr);
10619 Function *MapperFn =
10621 MapperFn->addFnAttr(Attribute::NoInline);
10622 MapperFn->addFnAttr(Attribute::NoUnwind);
10623 MapperFn->addParamAttr(0, Attribute::NoUndef);
10624 MapperFn->addParamAttr(1, Attribute::NoUndef);
10625 MapperFn->addParamAttr(2, Attribute::NoUndef);
10626 MapperFn->addParamAttr(3, Attribute::NoUndef);
10627 MapperFn->addParamAttr(4, Attribute::NoUndef);
10628 MapperFn->addParamAttr(5, Attribute::NoUndef);
10629
10630 // Start the mapper function code generation.
10631 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10633 Builder.SetInsertPoint(EntryBB);
10634 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
10635
10636 Value *MapperHandle = MapperFn->getArg(0);
10637 Value *BaseIn = MapperFn->getArg(1);
10638 Value *BeginIn = MapperFn->getArg(2);
10639 Value *Size = MapperFn->getArg(3);
10640 Value *MapType = MapperFn->getArg(4);
10641 Value *MapName = MapperFn->getArg(5);
10642
10643 // Compute the starting and end addresses of array elements.
10644 // Prepare common arguments for array initiation and deletion.
10645 // Convert the size in bytes into the number of array elements.
10646 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10647 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10648 Value *PtrBegin = BeginIn;
10649 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10650
10651 // Emit array initiation if this is an array section and \p MapType indicates
10652 // that memory allocation is required.
10653 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10654 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10655 MapType, MapName, ElementSize, HeadBB,
10656 /*IsInit=*/true);
10657
10658 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10659
10660 // Emit the loop header block.
10661 emitBlock(HeadBB, MapperFn);
10662 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10663 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10664 // Evaluate whether the initial condition is satisfied.
10665 Value *IsEmpty =
10666 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10667 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10668
10669 // Emit the loop body block.
10670 emitBlock(BodyBB, MapperFn);
10671 BasicBlock *LastBB = BodyBB;
10672 PHINode *PtrPHI =
10673 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10674 PtrPHI->addIncoming(PtrBegin, HeadBB);
10675
10676 // Get map clause information. Fill up the arrays with all mapped variables.
10677 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10678 if (!Info)
10679 return Info.takeError();
10680
10681 // Call the runtime API __tgt_mapper_num_components to get the number of
10682 // pre-existing components.
10683 Value *OffloadingArgs[] = {MapperHandle};
10684 Value *PreviousSize = createRuntimeFunctionCall(
10685 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10686 OffloadingArgs);
10687 Value *ShiftedPreviousSize =
10688 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10689
10690 // Fill up the runtime mapper handle for all components.
10691 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10692 Value *CurBaseArg = Info->BasePointers[I];
10693 Value *CurBeginArg = Info->Pointers[I];
10694 Value *CurSizeArg = Info->Sizes[I];
10695 Value *CurNameArg = Info->Names.size()
10696 ? Info->Names[I]
10697 : Constant::getNullValue(Builder.getPtrTy());
10698
10699 Value *OriMapType = Builder.getInt64(
10700 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10701 Info->Types[I]));
10702 auto RawType =
10703 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10704 Info->Types[I]);
10705 constexpr uint64_t MemberOfMask =
10706 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10707 constexpr uint64_t AttachBit =
10708 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10709 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10710
10711 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10712 // current array element (N = __tgt_mapper_num_components() at loop body
10713 // start).
10714 //
10715 // Example 1:
10716 // struct S { int x; int *p; };
10717 //
10718 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10719 // use: S arr[2]; ... map(arr)
10720 // entries per element:
10721 //
10722 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10723 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10724 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10725 //
10726 // Example 2:
10727 // struct S1 { int x; int y; };
10728 // struct S2 { int z; S1 *s1p; };
10729 //
10730 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10731 // s2.s1p->y)
10732 // use: S2 arr[2]; ... map(arr)
10733 // entries per element:
10734 //
10735 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10736 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10737 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10738 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10739 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10740 //
10741 // x/y carry inner MEMBER_OF(2)
10742 // which is shifted by N to become MEMBER_OF(N+2).
10743 //
10744 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10745 // the combined ALLOC entry for the s1p->x..y block, and the individual
10746 // x/y entries that are MEMBER_OF that block, all describe storage
10747 // reached through the attach ptr arr[i].s1p.
10748 //
10749 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10750 // linking them to the parent struct:
10751 //
10752 // * (*) Entries with HasAttachPtr: they represent pointee data that
10753 // occupies a different storage block than the struct being mapped, so
10754 // they are not a member of it. They may still be MEMBER_OF an entry
10755 // within that pointee block, in which case those pre-existing bits are
10756 // shifted -- see (***).
10757 // * (**) ATTACH entries: they are not a member of anything — they just
10758 // link a ptr to its ptee.
10759 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10760 // its pre-shaped entries already carry their final MEMBER_OF bits.
10761 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10762 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10763 // it.
10764 //
10765 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10766 // s1p->x/y entries above), those bits are still shifted by N.
10767 Value *MemberMapType;
10768 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10769 Info->HasAttachPtr[I]) {
10770 if (RawType & MemberOfMask)
10771 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10772 else
10773 MemberMapType = OriMapType;
10774 } else {
10775 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10776 }
10777
10778 // Combine the map type inherited from user-defined mapper with that
10779 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10780 // bits of the \a MapType, which is the input argument of the mapper
10781 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10782 // bits of MemberMapType.
10783 // [OpenMP 5.0], 1.2.6. map-type decay.
10784 // | alloc | to | from | tofrom | release | delete
10785 // ----------------------------------------------------------
10786 // alloc | alloc | alloc | alloc | alloc | release | delete
10787 // to | alloc | to | alloc | to | release | delete
10788 // from | alloc | alloc | from | from | release | delete
10789 // tofrom | alloc | to | from | tofrom | release | delete
10790 Value *LeftToFrom = Builder.CreateAnd(
10791 MapType,
10792 Builder.getInt64(
10793 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10794 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10795 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10796 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10797 BasicBlock *AllocElseBB =
10798 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10799 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10800 BasicBlock *ToElseBB =
10801 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10802 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10803 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10804 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10805 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10806 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10807 emitBlock(AllocBB, MapperFn);
10808 Value *AllocMapType = Builder.CreateAnd(
10809 MemberMapType,
10810 Builder.getInt64(
10811 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10812 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10813 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10814 Builder.CreateBr(EndBB);
10815 emitBlock(AllocElseBB, MapperFn);
10816 Value *IsTo = Builder.CreateICmpEQ(
10817 LeftToFrom,
10818 Builder.getInt64(
10819 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10820 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10821 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10822 // In case of to, clear OMP_MAP_FROM.
10823 emitBlock(ToBB, MapperFn);
10824 Value *ToMapType = Builder.CreateAnd(
10825 MemberMapType,
10826 Builder.getInt64(
10827 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10828 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10829 Builder.CreateBr(EndBB);
10830 emitBlock(ToElseBB, MapperFn);
10831 Value *IsFrom = Builder.CreateICmpEQ(
10832 LeftToFrom,
10833 Builder.getInt64(
10834 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10835 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10836 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10837 // In case of from, clear OMP_MAP_TO.
10838 emitBlock(FromBB, MapperFn);
10839 Value *FromMapType = Builder.CreateAnd(
10840 MemberMapType,
10841 Builder.getInt64(
10842 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10843 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10844 // In case of tofrom, do nothing.
10845 emitBlock(EndBB, MapperFn);
10846 LastBB = EndBB;
10847 PHINode *CurMapType =
10848 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10849 CurMapType->addIncoming(AllocMapType, AllocBB);
10850 CurMapType->addIncoming(ToMapType, ToBB);
10851 CurMapType->addIncoming(FromMapType, FromBB);
10852 CurMapType->addIncoming(MemberMapType, ToElseBB);
10853
10854 // Propagate map-type-modifying bits from the outer map clause to each map
10855 // inserted by the mapper.
10856 //
10857 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10858 // list item from the map clause and to apply the clauses specified in the
10859 // declared mapper to the construct on which the map clause appears...
10860 // If any modifier with the map-type-modifying property appears in the map
10861 // clause then the effect is as if that modifier appears in each map clause
10862 // specified in the declared mapper.
10863 //
10864 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10865 //
10866 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
10867 //
10868 // PRESENT is propagated only to entries that have an attach ptr
10869 // (HasAttachPtr): the pointee data, which occupies a different storage
10870 // block than the struct being mapped and so is not covered by the
10871 // present-check on the struct's own storage. A present modifier on the
10872 // outer clause must still require that pointee to be present on the device.
10873 //
10874 // This is gated on \p PropagatePresentToPointee (set by callers only for
10875 // OpenMP >= 6.0). Before 6.0 the present modifier is treated as not
10876 // applying to the pointee: the spec committee confirmed the divergence
10877 // between the present "motion" modifier (to/from) and the present map-type
10878 // modifier (map) was unintentional, to be fixed as an OpenMP 6.0 erratum,
10879 // so for 5.2 present is ignored for the pointee for both map and to/from.
10880 //
10881 // TODO: PRESENT should also be propagated to the struct's own members
10882 // (e.g. the s.x, s.y of map(present, mapper(id): s)) so that an absent
10883 // member triggers the present-check. We cannot do that yet: while pointer
10884 // members are mapped with PTR_AND_OBJ, a single combined entry allocates
10885 // the whole struct (including the pointer's storage), so propagating
10886 // PRESENT to it would wrongly require the pointer's pointee to be present.
10887 // Enable member propagation once Clang stops emitting PTR_AND_OBJ and uses
10888 // attach-style maps throughout.
10889 uint64_t ModifierBits =
10890 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10891 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
10892 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
10893 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
10894 if (PropagatePresentToPointee && Info->HasAttachPtr[I])
10895 ModifierBits |=
10896 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10897 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10898 Value *ImportedModifierBits =
10899 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
10900 Value *CurMapTypeWithModifiers = Builder.CreateOr(
10901 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
10902
10903 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
10904 // reserved for the attach(always) map-type modifier, and other modifier
10905 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
10906 Value *FinalMapType =
10907 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
10908
10909 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
10910 CurSizeArg, FinalMapType, CurNameArg};
10911
10912 auto ChildMapperFn = CustomMapperCB(I);
10913 if (!ChildMapperFn)
10914 return ChildMapperFn.takeError();
10915 if (*ChildMapperFn) {
10916 // Call the corresponding mapper function.
10917 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
10918 ->setDoesNotThrow();
10919 } else {
10920 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10921 // data structure.
10923 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10924 OffloadingArgs);
10925 }
10926 }
10927
10928 // Update the pointer to point to the next element that needs to be mapped,
10929 // and check whether we have mapped all elements.
10930 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
10931 "omp.arraymap.next");
10932 PtrPHI->addIncoming(PtrNext, LastBB);
10933 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
10934 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
10935 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
10936
10937 emitBlock(ExitBB, MapperFn);
10938 // Emit array deletion if this is an array section and \p MapType indicates
10939 // that deletion is required.
10940 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10941 MapType, MapName, ElementSize, DoneBB,
10942 /*IsInit=*/false);
10943
10944 // Emit the function exit block.
10945 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
10946
10947 Builder.CreateRetVoid();
10948 return MapperFn;
10949}
10950
10952 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
10953 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
10954 bool IsNonContiguous,
10955 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10956
10957 // Reset the array information.
10958 Info.clearArrayInfo();
10959 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
10960
10961 if (Info.NumberOfPtrs == 0)
10962 return Error::success();
10963
10964 Builder.restoreIP(AllocaIP);
10965 // Detect if we have any capture size requiring runtime evaluation of the
10966 // size so that a constant array could be eventually used.
10967 ArrayType *PointerArrayType =
10968 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
10969
10970 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
10971 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
10972
10973 Info.RTArgs.PointersArray = Builder.CreateAlloca(
10974 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
10975 AllocaInst *MappersArray = Builder.CreateAlloca(
10976 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
10977 Info.RTArgs.MappersArray = MappersArray;
10978
10979 // If we don't have any VLA types or other types that require runtime
10980 // evaluation, we can use a constant array for the map sizes, otherwise we
10981 // need to fill up the arrays as we do for the pointers.
10982 Type *Int64Ty = Builder.getInt64Ty();
10983 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
10984 ConstantInt::get(Int64Ty, 0));
10985 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
10986 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
10987 bool IsNonContigEntry =
10988 IsNonContiguous &&
10989 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10990 CombinedInfo.Types[I] &
10991 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
10992 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
10993 // descriptor_dim records), not the byte size.
10994 if (IsNonContigEntry) {
10995 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
10996 "Index must be in-bounds for NON_CONTIG Dims array");
10997 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
10998 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
10999 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
11000 continue;
11001 }
11002 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
11003 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
11004 ConstSizes[I] = CI;
11005 continue;
11006 }
11007 }
11008 RuntimeSizes.set(I);
11009 }
11010
11011 if (RuntimeSizes.all()) {
11012 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11013 Info.RTArgs.SizesArray = Builder.CreateAlloca(
11014 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11015 restoreIPandDebugLoc(Builder, CodeGenIP);
11016 } else {
11017 auto *SizesArrayInit = ConstantArray::get(
11018 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
11019 std::string Name = createPlatformSpecificName({"offload_sizes"});
11020 auto *SizesArrayGbl =
11021 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
11022 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
11023 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
11024
11025 if (!RuntimeSizes.any()) {
11026 Info.RTArgs.SizesArray = SizesArrayGbl;
11027 } else {
11028 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11029 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
11030 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11031 AllocaInst *Buffer = Builder.CreateAlloca(
11032 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11033 Buffer->setAlignment(OffloadSizeAlign);
11034 restoreIPandDebugLoc(Builder, CodeGenIP);
11035 Builder.CreateMemCpy(
11036 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11037 SizesArrayGbl, OffloadSizeAlign,
11038 Builder.getIntN(
11039 IndexSize,
11040 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11041
11042 Info.RTArgs.SizesArray = Buffer;
11043 }
11044 restoreIPandDebugLoc(Builder, CodeGenIP);
11045 }
11046
11047 // The map types are always constant so we don't need to generate code to
11048 // fill arrays. Instead, we create an array constant.
11050 for (auto mapFlag : CombinedInfo.Types)
11051 Mapping.push_back(
11052 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11053 mapFlag));
11054 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11055 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11056 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11057
11058 // The information types are only built if provided.
11059 if (!CombinedInfo.Names.empty()) {
11060 auto *MapNamesArrayGbl = createOffloadMapnames(
11061 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11062 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11063 Info.EmitDebug = true;
11064 } else {
11065 Info.RTArgs.MapNamesArray =
11067 Info.EmitDebug = false;
11068 }
11069
11070 // If there's a present map type modifier, it must not be applied to the end
11071 // of a region, so generate a separate map type array in that case.
11072 if (Info.separateBeginEndCalls()) {
11073 bool EndMapTypesDiffer = false;
11074 for (uint64_t &Type : Mapping) {
11075 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11076 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11077 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11078 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11079 EndMapTypesDiffer = true;
11080 }
11081 }
11082 if (EndMapTypesDiffer) {
11083 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11084 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11085 }
11086 }
11087
11088 PointerType *PtrTy = Builder.getPtrTy();
11089 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11090 Value *BPVal = CombinedInfo.BasePointers[I];
11091 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11092 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11093 0, I);
11094 Builder.CreateAlignedStore(BPVal, BP,
11095 M.getDataLayout().getPrefTypeAlign(PtrTy));
11096
11097 if (Info.requiresDevicePointerInfo()) {
11098 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11099 CodeGenIP = Builder.saveIP();
11100 Builder.restoreIP(AllocaIP);
11101 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11102 restoreIPandDebugLoc(Builder, CodeGenIP);
11103 if (DeviceAddrCB)
11104 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11105 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11106 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11107 if (DeviceAddrCB)
11108 DeviceAddrCB(I, BP);
11109 }
11110 }
11111
11112 Value *PVal = CombinedInfo.Pointers[I];
11113 Value *P = Builder.CreateConstInBoundsGEP2_32(
11114 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11115 I);
11116 // TODO: Check alignment correct.
11117 Builder.CreateAlignedStore(PVal, P,
11118 M.getDataLayout().getPrefTypeAlign(PtrTy));
11119
11120 if (RuntimeSizes.test(I)) {
11121 Value *S = Builder.CreateConstInBoundsGEP2_32(
11122 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11123 /*Idx0=*/0,
11124 /*Idx1=*/I);
11125 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11126 Int64Ty,
11127 /*isSigned=*/true),
11128 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11129 }
11130 // Fill up the mapper array.
11131 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11132 Value *MFunc = ConstantPointerNull::get(PtrTy);
11133
11134 auto CustomMFunc = CustomMapperCB(I);
11135 if (!CustomMFunc)
11136 return CustomMFunc.takeError();
11137 if (*CustomMFunc)
11138 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11139
11140 Value *MAddr = Builder.CreateInBoundsGEP(
11141 PointerArrayType, MappersArray,
11142 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11143 Builder.CreateAlignedStore(
11144 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11145 }
11146
11147 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11148 Info.NumberOfPtrs == 0)
11149 return Error::success();
11150 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11151 return Error::success();
11152}
11153
11155 BasicBlock *CurBB = Builder.GetInsertBlock();
11156
11157 if (!CurBB || CurBB->hasTerminator()) {
11158 // If there is no insert point or the previous block is already
11159 // terminated, don't touch it.
11160 } else {
11161 // Otherwise, create a fall-through branch.
11162 Builder.CreateBr(Target);
11163 }
11164
11165 Builder.ClearInsertionPoint();
11166}
11167
11169 bool IsFinished) {
11170 BasicBlock *CurBB = Builder.GetInsertBlock();
11171
11172 // Fall out of the current block (if necessary).
11173 emitBranch(BB);
11174
11175 if (IsFinished && BB->use_empty()) {
11176 BB->eraseFromParent();
11177 return;
11178 }
11179
11180 // Place the block after the current block, if possible, or else at
11181 // the end of the function.
11182 if (CurBB && CurBB->getParent())
11183 CurFn->insert(std::next(CurBB->getIterator()), BB);
11184 else
11185 CurFn->insert(CurFn->end(), BB);
11186 Builder.SetInsertPoint(BB);
11187}
11188
11190 BodyGenCallbackTy ElseGen,
11191 InsertPointTy AllocaIP,
11192 ArrayRef<BasicBlock *> DeallocBlocks) {
11193 // If the condition constant folds and can be elided, try to avoid emitting
11194 // the condition and the dead arm of the if/else.
11195 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11196 auto CondConstant = CI->getSExtValue();
11197 if (CondConstant)
11198 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11199
11200 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11201 }
11202
11203 Function *CurFn = Builder.GetInsertBlock()->getParent();
11204
11205 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11206 // emit the conditional branch.
11207 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11208 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11209 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11210 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11211 // Emit the 'then' code.
11212 emitBlock(ThenBlock, CurFn);
11213 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11214 return Err;
11215 emitBranch(ContBlock);
11216 // Emit the 'else' code if present.
11217 // There is no need to emit line number for unconditional branch.
11218 emitBlock(ElseBlock, CurFn);
11219 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11220 return Err;
11221 // There is no need to emit line number for unconditional branch.
11222 emitBranch(ContBlock);
11223 // Emit the continuation block for code after the if.
11224 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11225 return Error::success();
11226}
11227
11228bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11229 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11232 "Unexpected Atomic Ordering.");
11233
11234 bool Flush = false;
11236
11237 switch (AK) {
11238 case Read:
11241 FlushAO = AtomicOrdering::Acquire;
11242 Flush = true;
11243 }
11244 break;
11245 case Write:
11246 case Compare:
11247 case Update:
11250 FlushAO = AtomicOrdering::Release;
11251 Flush = true;
11252 }
11253 break;
11254 case Capture:
11255 switch (AO) {
11257 FlushAO = AtomicOrdering::Acquire;
11258 Flush = true;
11259 break;
11261 FlushAO = AtomicOrdering::Release;
11262 Flush = true;
11263 break;
11267 Flush = true;
11268 break;
11269 default:
11270 // do nothing - leave silently.
11271 break;
11272 }
11273 }
11274
11275 if (Flush) {
11276 // Currently Flush RT call still doesn't take memory_ordering, so for when
11277 // that happens, this tries to do the resolution of which atomic ordering
11278 // to use with but issue the flush call
11279 // TODO: pass `FlushAO` after memory ordering support is added
11280 (void)FlushAO;
11281 emitFlush(Loc);
11282 }
11283
11284 // for AO == AtomicOrdering::Monotonic and all other case combinations
11285 // do nothing
11286 return Flush;
11287}
11288
11292 AtomicOrdering AO, InsertPointTy AllocaIP) {
11293 if (!updateToLocation(Loc))
11294 return Loc.IP;
11295
11296 assert(X.Var->getType()->isPointerTy() &&
11297 "OMP Atomic expects a pointer to target memory");
11298 Type *XElemTy = X.ElemTy;
11299 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11300 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11301 "OMP atomic read expected a scalar type");
11302
11303 Value *XRead = nullptr;
11304
11305 if (XElemTy->isIntegerTy()) {
11306 LoadInst *XLD =
11307 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11308 XLD->setAtomic(AO);
11309 XRead = cast<Value>(XLD);
11310 } else if (XElemTy->isStructTy()) {
11311 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11312 // target does not support `atomicrmw` of the size of the struct
11313 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11314 OldVal->setAtomic(AO);
11315 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11316 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11317 OpenMPIRBuilder::AtomicInfo atomicInfo(
11318 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11319 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11320 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11321 XRead = AtomicLoadRes.first;
11322 OldVal->eraseFromParent();
11323 } else {
11324 // We need to perform atomic op as integer
11325 IntegerType *IntCastTy =
11326 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11327 LoadInst *XLoad =
11328 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11329 XLoad->setAtomic(AO);
11330 if (XElemTy->isFloatingPointTy()) {
11331 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11332 } else {
11333 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11334 }
11335 }
11336 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11337 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11338 return Builder.saveIP();
11339}
11340
11343 AtomicOpValue &X, Value *Expr,
11344 AtomicOrdering AO, InsertPointTy AllocaIP) {
11345 if (!updateToLocation(Loc))
11346 return Loc.IP;
11347
11348 assert(X.Var->getType()->isPointerTy() &&
11349 "OMP Atomic expects a pointer to target memory");
11350 Type *XElemTy = X.ElemTy;
11351 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11352 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11353 "OMP atomic write expected a scalar type");
11354
11355 if (XElemTy->isIntegerTy()) {
11356 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11357 XSt->setAtomic(AO);
11358 } else if (XElemTy->isStructTy()) {
11359 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11360 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11361 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11362 OpenMPIRBuilder::AtomicInfo atomicInfo(
11363 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11364 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11365 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11366 OldVal->eraseFromParent();
11367 } else {
11368 // We need to bitcast and perform atomic op as integers
11369 IntegerType *IntCastTy =
11370 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11371 Value *ExprCast =
11372 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11373 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11374 XSt->setAtomic(AO);
11375 }
11376
11377 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11378 return Builder.saveIP();
11379}
11380
11383 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11384 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11385 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11386 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11387 if (!updateToLocation(Loc))
11388 return Loc.IP;
11389
11390 LLVM_DEBUG({
11391 Type *XTy = X.Var->getType();
11392 assert(XTy->isPointerTy() &&
11393 "OMP Atomic expects a pointer to target memory");
11394 Type *XElemTy = X.ElemTy;
11395 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11396 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11397 "OMP atomic update expected a scalar or struct type");
11398 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11399 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&
11400 "OpenMP atomic does not support LT or GT operations");
11401 });
11402
11403 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11404 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11405 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11406 if (!AtomicResult)
11407 return AtomicResult.takeError();
11408 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11409 return Builder.saveIP();
11410}
11411
11412// FIXME: Duplicating AtomicExpand
11413Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11414 AtomicRMWInst::BinOp RMWOp) {
11415 switch (RMWOp) {
11416 case AtomicRMWInst::Add:
11417 return Builder.CreateAdd(Src1, Src2);
11418 case AtomicRMWInst::Sub:
11419 return Builder.CreateSub(Src1, Src2);
11420 case AtomicRMWInst::And:
11421 return Builder.CreateAnd(Src1, Src2);
11423 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11424 case AtomicRMWInst::Or:
11425 return Builder.CreateOr(Src1, Src2);
11426 case AtomicRMWInst::Xor:
11427 return Builder.CreateXor(Src1, Src2);
11432 case AtomicRMWInst::Max:
11433 case AtomicRMWInst::Min:
11446 llvm_unreachable("Unsupported atomic update operation");
11447 }
11448 llvm_unreachable("Unsupported atomic update operation");
11449}
11450
11452 // Loads cannot use Release or AcquireRelease ordering. This load is
11453 // just the initial value for the cmpxchg loop; the cmpxchg itself
11454 // retains the original ordering.
11455 AtomicOrdering LoadAO = AO;
11456
11457 if (AO == AtomicOrdering::Release) {
11459 } else if (AO == AtomicOrdering::AcquireRelease) {
11460 LoadAO = AtomicOrdering::Acquire;
11461 }
11462
11463 return LoadAO;
11464}
11465
11466Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11467 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11469 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11470 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11471 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11472 bool emitRMWOp = false;
11473 switch (RMWOp) {
11474 case AtomicRMWInst::Add:
11475 case AtomicRMWInst::And:
11477 case AtomicRMWInst::Or:
11478 case AtomicRMWInst::Xor:
11480 emitRMWOp = XElemTy;
11481 break;
11482 case AtomicRMWInst::Sub:
11483 emitRMWOp = (IsXBinopExpr && XElemTy);
11484 break;
11485 default:
11486 emitRMWOp = false;
11487 }
11488 emitRMWOp &= XElemTy->isIntegerTy();
11489
11490 std::pair<Value *, Value *> Res;
11491 if (emitRMWOp) {
11492 AtomicRMWInst *RMWInst =
11493 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11494 if (T.isAMDGPU()) {
11495 if (IsIgnoreDenormalMode)
11496 RMWInst->setMetadata("amdgpu.ignore.denormal.mode",
11497 llvm::MDNode::get(Builder.getContext(), {}));
11498 if (!IsFineGrainedMemory)
11499 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11500 llvm::MDNode::get(Builder.getContext(), {}));
11501 if (!IsRemoteMemory)
11502 RMWInst->setMetadata("amdgpu.no.remote.memory",
11503 llvm::MDNode::get(Builder.getContext(), {}));
11504 }
11505 Res.first = RMWInst;
11506 // not needed except in case of postfix captures. Generate anyway for
11507 // consistency with the else part. Will be removed with any DCE pass.
11508 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11509 if (RMWOp == AtomicRMWInst::Xchg)
11510 Res.second = Res.first;
11511 else
11512 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11513 } else if (XElemTy->isStructTy()) {
11514 LoadInst *OldVal =
11515 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11517 OldVal->setAtomic(LoadAO);
11518 const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
11519 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11520
11521 OpenMPIRBuilder::AtomicInfo atomicInfo(
11522 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11523 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11524 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11525 BasicBlock *CurBB = Builder.GetInsertBlock();
11526 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11527 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11528 BasicBlock *ExitBB =
11529 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11530 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11531 X->getName() + ".atomic.cont");
11532 ContBB->getTerminator()->eraseFromParent();
11533 Builder.restoreIP(AllocaIP);
11534 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11535 NewAtomicAddr->setName(X->getName() + "x.new.val");
11536 Builder.SetInsertPoint(ContBB);
11537 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11538 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11539 Value *OldExprVal = PHI;
11540 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11541 if (!CBResult)
11542 return CBResult.takeError();
11543 Value *Upd = *CBResult;
11544 Builder.CreateStore(Upd, NewAtomicAddr);
11547 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11548 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11549 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11550 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11551 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11552 OldVal->eraseFromParent();
11553 Res.first = OldExprVal;
11554 Res.second = Upd;
11555
11556 if (UnreachableInst *ExitTI =
11558 CurBBTI->eraseFromParent();
11559 Builder.SetInsertPoint(ExitBB);
11560 } else {
11561 Builder.SetInsertPoint(ExitTI);
11562 }
11563 } else {
11564 IntegerType *IntCastTy =
11565 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11566 LoadInst *OldVal =
11567 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11569 OldVal->setAtomic(LoadAO);
11570 // CurBB
11571 // | /---\
11572 // ContBB |
11573 // | \---/
11574 // ExitBB
11575 BasicBlock *CurBB = Builder.GetInsertBlock();
11576 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11577 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11578 BasicBlock *ExitBB =
11579 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11580 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11581 X->getName() + ".atomic.cont");
11582 ContBB->getTerminator()->eraseFromParent();
11583 Builder.restoreIP(AllocaIP);
11584 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11585 NewAtomicAddr->setName(X->getName() + "x.new.val");
11586 Builder.SetInsertPoint(ContBB);
11587 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11588 PHI->addIncoming(OldVal, CurBB);
11589 bool IsIntTy = XElemTy->isIntegerTy();
11590 Value *OldExprVal = PHI;
11591 if (!IsIntTy) {
11592 if (XElemTy->isFloatingPointTy()) {
11593 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11594 X->getName() + ".atomic.fltCast");
11595 } else {
11596 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11597 X->getName() + ".atomic.ptrCast");
11598 }
11599 }
11600
11601 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11602 if (!CBResult)
11603 return CBResult.takeError();
11604 Value *Upd = *CBResult;
11605 Builder.CreateStore(Upd, NewAtomicAddr);
11606 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11609 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11610 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11611 Result->setVolatile(VolatileX);
11612 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11613 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11614 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11615 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11616
11617 Res.first = OldExprVal;
11618 Res.second = Upd;
11619
11620 // set Insertion point in exit block
11621 if (UnreachableInst *ExitTI =
11623 CurBBTI->eraseFromParent();
11624 Builder.SetInsertPoint(ExitBB);
11625 } else {
11626 Builder.SetInsertPoint(ExitTI);
11627 }
11628 }
11629
11630 return Res;
11631}
11632
11635 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11636 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11637 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11638 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11639 if (!updateToLocation(Loc))
11640 return Loc.IP;
11641
11642 LLVM_DEBUG({
11643 Type *XTy = X.Var->getType();
11644 assert(XTy->isPointerTy() &&
11645 "OMP Atomic expects a pointer to target memory");
11646 Type *XElemTy = X.ElemTy;
11647 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11648 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11649 "OMP atomic capture expected a scalar or struct type");
11650 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11651 "OpenMP atomic does not support LT or GT operations");
11652 });
11653
11654 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11655 // 'x' is simply atomically rewritten with 'expr'.
11656 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11657 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11658 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11659 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11660 if (!AtomicResult)
11661 return AtomicResult.takeError();
11662 Value *CapturedVal =
11663 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11664 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11665
11666 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11667 return Builder.saveIP();
11668}
11669
11673 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11674 bool IsFailOnly, bool IsWeak) {
11675
11677 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11678 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11679}
11680
11684 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11685 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11686
11687 if (!updateToLocation(Loc))
11688 return Loc.IP;
11689
11690 assert(X.Var->getType()->isPointerTy() &&
11691 "OMP atomic expects a pointer to target memory");
11692 // compare capture
11693 if (V.Var) {
11694 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11695 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11696 }
11697
11698 bool IsInteger = E->getType()->isIntegerTy();
11699
11700 if (Op == OMPAtomicCompareOp::EQ) {
11701 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11702 // R.Var handling.
11703 Value *OldValue = nullptr;
11704 Value *SuccessOrFail = nullptr;
11705
11706 if (!IsInteger && HandleFPNegZero) {
11707 // IEEE 754 special cases for cmpxchg (which is bitwise):
11708 // 1. -0.0 == +0.0 but they have different bit patterns.
11709 // 2. NaN != NaN but identical NaN bit patterns would match.
11710 //
11711 // CurBB:
11712 // %e_int = bitcast E to intN
11713 // %d_int = bitcast D to intN
11714 // %x_curr = load atomic intN, X
11715 // %x_fp = bitcast %x_curr to FP
11716 // %e_is_nan = fcmp uno E, E
11717 // %x_is_nan = fcmp uno %x_fp, %x_fp
11718 // %either_nan = or %e_is_nan, %x_is_nan
11719 // br %either_nan, NaNBB, NotNaNBB
11720 // NaNBB: ; NaN == anything is always false
11721 // br ExitBB
11722 // NotNaNBB:
11723 // %x_is_zero = fcmp oeq %x_fp, 0.0
11724 // %e_is_zero = fcmp oeq E, 0.0
11725 // %both_zero = and %x_is_zero, %e_is_zero
11726 // br %both_zero, ZeroBB, NormalBB
11727 // ZeroBB: ; both ±0.0 → x = d
11728 // cmpxchg X, %x_curr, %d_int
11729 // br ExitBB
11730 // NormalBB: ; original path
11731 // cmpxchg X, %e_int, %d_int
11732 // br ExitBB
11733 // ExitBB:
11734 // phi merge
11735 IntegerType *IntCastTy =
11736 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11737 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11738 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11739
11740 // Load X atomically.
11741 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11742 X.Var->getName() + ".atomic.load");
11744 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11745
11746 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11747 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11748 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11749 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11750 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11751
11752 BasicBlock *CurBB = Builder.GetInsertBlock();
11753 Function *F = CurBB->getParent();
11754 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11755 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11756 BasicBlock *ExitBB =
11757 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11759 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11760 BasicBlock *NotNaNBB = BasicBlock::Create(
11761 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11763 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11764 BasicBlock *NormalBB = BasicBlock::Create(
11765 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11766
11767 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11768 CurBB->getTerminator()->eraseFromParent();
11769 Builder.SetInsertPoint(CurBB);
11770 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11771
11772 // NaNBB: NaN == anything is always false; skip cmpxchg.
11773 Builder.SetInsertPoint(NaNBB);
11774 Builder.CreateBr(ExitBB);
11775
11776 // NotNaNBB: check both X and E for ±0.0.
11777 Builder.SetInsertPoint(NotNaNBB);
11778 Value *XIsZero =
11779 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11780 X.Var->getName() + ".atomic.xiszero");
11781 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11782 "atomic.e.iszero");
11783 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11784 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11785
11786 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11787 Builder.SetInsertPoint(ZeroBB);
11788 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11789 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11790 ResZero->setWeak(IsWeak);
11791 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11792 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11793 Builder.CreateBr(ExitBB);
11794
11795 // NormalBB: original bitwise cmpxchg.
11796 Builder.SetInsertPoint(NormalBB);
11797 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11798 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11799 ResNormal->setWeak(IsWeak);
11800 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11801 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11802 Builder.CreateBr(ExitBB);
11803
11804 // ExitBB: merge results from NaN, Zero, and Normal paths.
11805 Builder.SetInsertPoint(ExitBB, ExitBB->begin());
11806 PHINode *OldIntPHI =
11807 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11808 OldIntPHI->addIncoming(XCurr, NaNBB);
11809 OldIntPHI->addIncoming(OldZero, ZeroBB);
11810 OldIntPHI->addIncoming(OldNormal, NormalBB);
11811 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11812 X.Var->getName() + ".atomic.ok");
11813 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11814 SuccessPHI->addIncoming(OkZero, ZeroBB);
11815 SuccessPHI->addIncoming(OkNormal, NormalBB);
11816
11817 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11818 CurBBTI->eraseFromParent();
11819 Builder.SetInsertPoint(ExitBB);
11820 } else {
11821 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11822 }
11823
11824 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11825 X.Var->getName() + ".atomic.old.fp");
11826 SuccessOrFail = SuccessPHI;
11827 } else {
11828 AtomicCmpXchgInst *Result = nullptr;
11829 if (!IsInteger) {
11830 IntegerType *IntCastTy =
11831 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11832 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11833 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11834 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11835 MaybeAlign(), AO, Failure);
11836 } else {
11837 Result =
11838 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11839 }
11840 Result->setWeak(IsWeak);
11841
11842 if (V.Var) {
11843 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11844 if (!IsInteger)
11845 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11846 assert(OldValue->getType() == V.ElemTy &&
11847 "OldValue and V must be of same type");
11848 if (IsPostfixUpdate) {
11849 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11850 } else {
11851 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11852 if (IsFailOnly) {
11853 BasicBlock *CurBB = Builder.GetInsertBlock();
11854 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11855 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11856 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11857 CurBBTI, X.Var->getName() + ".atomic.exit");
11858 BasicBlock *ContBB = CurBB->splitBasicBlock(
11859 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11860 ContBB->getTerminator()->eraseFromParent();
11861 CurBB->getTerminator()->eraseFromParent();
11862
11863 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11864
11865 Builder.SetInsertPoint(ContBB);
11866 Builder.CreateStore(OldValue, V.Var);
11867 Builder.CreateBr(ExitBB);
11868
11869 if (UnreachableInst *ExitTI =
11871 CurBBTI->eraseFromParent();
11872 Builder.SetInsertPoint(ExitBB);
11873 } else {
11874 Builder.SetInsertPoint(ExitTI);
11875 }
11876 } else {
11877 Value *CapturedValue =
11878 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11879 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11880 }
11881 }
11882 }
11883 // The comparison result has to be stored.
11884 if (R.Var) {
11885 assert(R.Var->getType()->isPointerTy() &&
11886 "r.var must be of pointer type");
11887 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11888
11889 Value *SuccessFailureVal =
11890 Builder.CreateExtractValue(Result, /*Idxs=*/1);
11891 Value *ResultCast =
11892 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
11893 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
11894 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11895 }
11896 }
11897
11898 // For the HandleFPNegZero path, handle V.Var and R.Var using the
11899 // pre-computed OldValue and SuccessOrFail.
11900 if (HandleFPNegZero && !IsInteger) {
11901 if (V.Var) {
11902 assert(OldValue->getType() == V.ElemTy &&
11903 "OldValue and V must be of same type");
11904 if (IsPostfixUpdate) {
11905 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11906 } else {
11907 if (IsFailOnly) {
11908 BasicBlock *CurBB = Builder.GetInsertBlock();
11909 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11910 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11911 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11912 CurBBTI, X.Var->getName() + ".atomic.exit");
11913 BasicBlock *ContBB = CurBB->splitBasicBlock(
11914 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11915 ContBB->getTerminator()->eraseFromParent();
11916 CurBB->getTerminator()->eraseFromParent();
11917
11918 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11919
11920 Builder.SetInsertPoint(ContBB);
11921 Builder.CreateStore(OldValue, V.Var);
11922 Builder.CreateBr(ExitBB);
11923
11924 if (UnreachableInst *ExitTI =
11926 CurBBTI->eraseFromParent();
11927 Builder.SetInsertPoint(ExitBB);
11928 } else {
11929 Builder.SetInsertPoint(ExitTI);
11930 }
11931 } else {
11932 Value *CapturedValue =
11933 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11934 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11935 }
11936 }
11937 }
11938 // The comparison result has to be stored.
11939 if (R.Var) {
11940 assert(R.Var->getType()->isPointerTy() &&
11941 "r.var must be of pointer type");
11942 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11943
11944 Value *ResultCast = R.IsSigned
11945 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
11946 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
11947 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11948 }
11949 }
11950 } else {
11951 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
11952 "Op should be either max or min at this point");
11953 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
11954
11955 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
11956 // Let's take max as example.
11957 // OpenMP form:
11958 // x = x > expr ? expr : x;
11959 // LLVM form:
11960 // *ptr = *ptr > val ? *ptr : val;
11961 // We need to transform to LLVM form.
11962 // x = x <= expr ? x : expr;
11964 if (IsXBinopExpr) {
11965 if (IsInteger) {
11966 if (X.IsSigned)
11967 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
11969 else
11970 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
11972 } else {
11973 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
11975 }
11976 } else {
11977 if (IsInteger) {
11978 if (X.IsSigned)
11979 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
11981 else
11982 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
11984 } else {
11985 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
11987 }
11988 }
11989
11990 AtomicRMWInst *OldValue =
11991 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
11992 if (V.Var) {
11993 Value *CapturedValue = nullptr;
11994 if (IsPostfixUpdate) {
11995 CapturedValue = OldValue;
11996 } else {
11997 CmpInst::Predicate Pred;
11998 switch (NewOp) {
11999 case AtomicRMWInst::Max:
12000 Pred = CmpInst::ICMP_SGT;
12001 break;
12003 Pred = CmpInst::ICMP_UGT;
12004 break;
12006 Pred = CmpInst::FCMP_OGT;
12007 break;
12008 case AtomicRMWInst::Min:
12009 Pred = CmpInst::ICMP_SLT;
12010 break;
12012 Pred = CmpInst::ICMP_ULT;
12013 break;
12015 Pred = CmpInst::FCMP_OLT;
12016 break;
12017 default:
12018 llvm_unreachable("unexpected comparison op");
12019 }
12020 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
12021 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
12022 }
12023 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12024 }
12025 }
12026
12027 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
12028
12029 return Builder.saveIP();
12030}
12031
12034 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12035 Value *NumTeamsUpper, Value *ThreadLimit,
12036 Value *IfExpr) {
12037 if (!updateToLocation(Loc))
12038 return InsertPointTy();
12039
12040 uint32_t SrcLocStrSize;
12041 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12042 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12043 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12044
12045 // Outer allocation basicblock is the entry block of the current function.
12046 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12047 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12048 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12049 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12050 }
12051
12052 // The current basic block is split into four basic blocks. After outlining,
12053 // they will be mapped as follows:
12054 // ```
12055 // def current_fn() {
12056 // current_basic_block:
12057 // br label %teams.exit
12058 // teams.exit:
12059 // ; instructions after teams
12060 // }
12061 //
12062 // def outlined_fn() {
12063 // teams.alloca:
12064 // br label %teams.body
12065 // teams.body:
12066 // ; instructions within teams body
12067 // }
12068 // ```
12069 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12070 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12071 BasicBlock *AllocaBB =
12072 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12073
12074 bool SubClausesPresent =
12075 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12076 // Push num_teams
12077 if (!Config.isTargetDevice() && SubClausesPresent) {
12078 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12079 "if lowerbound is non-null, then upperbound must also be non-null "
12080 "for bounds on num_teams");
12081
12082 if (NumTeamsUpper == nullptr)
12083 NumTeamsUpper = Builder.getInt32(0);
12084
12085 if (NumTeamsLower == nullptr)
12086 NumTeamsLower = NumTeamsUpper;
12087
12088 if (IfExpr) {
12089 assert(IfExpr->getType()->isIntegerTy() &&
12090 "argument to if clause must be an integer value");
12091
12092 // upper = ifexpr ? upper : 1
12093 if (IfExpr->getType() != Int1)
12094 IfExpr = Builder.CreateICmpNE(IfExpr,
12095 ConstantInt::get(IfExpr->getType(), 0));
12096 NumTeamsUpper = Builder.CreateSelect(
12097 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12098
12099 // lower = ifexpr ? lower : 1
12100 NumTeamsLower = Builder.CreateSelect(
12101 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12102 }
12103
12104 if (ThreadLimit == nullptr)
12105 ThreadLimit = Builder.getInt32(0);
12106
12107 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12108 // truncate or sign extend the passed values to match the int32 parameters.
12109 Value *NumTeamsLowerInt32 =
12110 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12111 Value *NumTeamsUpperInt32 =
12112 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12113 Value *ThreadLimitInt32 =
12114 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12115
12116 Value *ThreadNum = getOrCreateThreadID(Ident);
12117
12119 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12120 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12121 ThreadLimitInt32});
12122 }
12123 // Generate the body of teams.
12124 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12125 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12126 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12127 return Err;
12128
12129 auto OI = std::make_unique<OutlineInfo>();
12130 OI->EntryBB = AllocaBB;
12131 OI->ExitBB = ExitBB;
12132 OI->OuterAllocBB = &OuterAllocaBB;
12133
12134 // Insert fake values for global tid and bound tid.
12136 InsertPointTy OuterAllocaIP(&OuterAllocaBB, OuterAllocaBB.begin());
12137 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12138 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12139 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12140 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12141
12142 auto HostPostOutlineCB = [this, Ident,
12143 ToBeDeleted](Function &OutlinedFn) mutable {
12144 // The stale call instruction will be replaced with a new call instruction
12145 // for runtime call with the outlined function.
12146
12147 assert(OutlinedFn.hasOneUse() &&
12148 "there must be a single user for the outlined function");
12149 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12150 ToBeDeleted.push_back(StaleCI);
12151
12152 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12153 "Outlined function must have two or three arguments only");
12154
12155 bool HasShared = OutlinedFn.arg_size() == 3;
12156
12157 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12158 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12159 if (HasShared)
12160 OutlinedFn.getArg(2)->setName("data");
12161
12162 // Call to the runtime function for teams in the current function.
12163 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12164 "outlined function.");
12165 Builder.SetInsertPoint(StaleCI);
12166 SmallVector<Value *> Args = {
12167 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12168 if (HasShared)
12169 Args.push_back(StaleCI->getArgOperand(2));
12172 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12173 Args);
12174
12175 Builder.ClearInsertionPoint();
12176 for (Instruction *I : llvm::reverse(ToBeDeleted))
12177 I->eraseFromParent();
12178 };
12179
12180 if (!Config.isTargetDevice())
12181 OI->PostOutlineCB = HostPostOutlineCB;
12182
12183 addOutlineInfo(std::move(OI));
12184
12185 Builder.SetInsertPoint(ExitBB);
12186
12187 return Builder.saveIP();
12188}
12189
12191 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12192 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12193 if (!updateToLocation(Loc))
12194 return InsertPointTy();
12195
12196 BasicBlock *OuterAllocaBB = OuterAllocIP.getBlock();
12197
12198 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12199 BasicBlock *BodyBB =
12200 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12201 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12202 }
12203 BasicBlock *ExitBB =
12204 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12205 BasicBlock *BodyBB =
12206 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12207 BasicBlock *AllocaBB =
12208 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12209
12210 // Generate the body of distribute clause
12211 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12212 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12213 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12214 return Err;
12215
12216 // When using target we use different runtime functions which require a
12217 // callback.
12218 if (Config.isTargetDevice()) {
12219 auto OI = std::make_unique<OutlineInfo>();
12220 OI->OuterAllocBB = OuterAllocIP.getBlock();
12221 OI->EntryBB = AllocaBB;
12222 OI->ExitBB = ExitBB;
12223 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12224 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12225
12226 addOutlineInfo(std::move(OI));
12227 }
12228 Builder.SetInsertPoint(ExitBB);
12229
12230 return Builder.saveIP();
12231}
12232
12235 std::string VarName) {
12236 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12238 Names.size()),
12239 Names);
12240 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12241 M, MapNamesArrayInit->getType(),
12242 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12243 VarName);
12244 return MapNamesArrayGlobal;
12245}
12246
12247// Create all simple and struct types exposed by the runtime and remember
12248// the llvm::PointerTypes of them for easy access later.
12249void OpenMPIRBuilder::initializeTypes(Module &M) {
12250 LLVMContext &Ctx = M.getContext();
12251 StructType *T;
12252 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12253 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12254#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12255#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12256 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12257 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12258#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12259 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12260 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12261#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12262 T = StructType::getTypeByName(Ctx, StructName); \
12263 if (!T) \
12264 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12265 VarName = T; \
12266 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12267#include "llvm/Frontend/OpenMP/OMPKinds.def"
12268}
12269
12272 SmallVectorImpl<BasicBlock *> &BlockVector) {
12274 BlockSet.insert(EntryBB);
12275 BlockSet.insert(ExitBB);
12276
12277 Worklist.push_back(EntryBB);
12278 while (!Worklist.empty()) {
12279 BasicBlock *BB = Worklist.pop_back_val();
12280 BlockVector.push_back(BB);
12281 for (BasicBlock *SuccBB : successors(BB))
12282 if (BlockSet.insert(SuccBB).second)
12283 Worklist.push_back(SuccBB);
12284 }
12285}
12286
12287std::unique_ptr<CodeExtractor>
12289 bool ArgsInZeroAddressSpace,
12290 Twine Suffix) {
12291 return std::make_unique<CodeExtractor>(
12292 Blocks, /* DominatorTree */ nullptr,
12293 /* AggregateArgs */ true,
12294 /* BlockFrequencyInfo */ nullptr,
12295 /* BranchProbabilityInfo */ nullptr,
12296 /* AssumptionCache */ nullptr,
12297 /* AllowVarArgs */ true,
12298 /* AllowAlloca */ true,
12299 /* AllocationBlock*/ OuterAllocBB,
12300 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12301 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12302}
12303
12304std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12305 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12306 return std::make_unique<DeviceSharedMemCodeExtractor>(
12307 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12308 /* AggregateArgs */ true,
12309 /* BlockFrequencyInfo */ nullptr,
12310 /* BranchProbabilityInfo */ nullptr,
12311 /* AssumptionCache */ nullptr,
12312 /* AllowVarArgs */ true,
12313 /* AllowAlloca */ true,
12314 /* AllocationBlock*/ OuterAllocBB,
12315 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12317 : OuterDeallocBBs,
12318 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12319}
12320
12322 uint64_t Size, int32_t Flags,
12324 StringRef Name) {
12325 if (!Config.isGPU()) {
12328 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12329 return;
12330 }
12331 // TODO: Add support for global variables on the device after declare target
12332 // support.
12333 Function *Fn = dyn_cast<Function>(Addr);
12334 if (!Fn)
12335 return;
12336
12337 // Add a function attribute for the kernel.
12338 Fn->addFnAttr("kernel");
12339 if (T.isAMDGCN())
12340 Fn->addFnAttr("uniform-work-group-size");
12341 Fn->addFnAttr(Attribute::MustProgress);
12342}
12343
12344// We only generate metadata for function that contain target regions.
12347
12348 // If there are no entries, we don't need to do anything.
12349 if (OffloadInfoManager.empty())
12350 return;
12351
12352 LLVMContext &C = M.getContext();
12355 16>
12356 OrderedEntries(OffloadInfoManager.size());
12357
12358 // Auxiliary methods to create metadata values and strings.
12359 auto &&GetMDInt = [this](unsigned V) {
12360 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12361 };
12362
12363 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12364
12365 // Create the offloading info metadata node.
12366 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12367 auto &&TargetRegionMetadataEmitter =
12368 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12369 const TargetRegionEntryInfo &EntryInfo,
12371 // Generate metadata for target regions. Each entry of this metadata
12372 // contains:
12373 // - Entry 0 -> Kind of this type of metadata (0).
12374 // - Entry 1 -> Device ID of the file where the entry was identified.
12375 // - Entry 2 -> File ID of the file where the entry was identified.
12376 // - Entry 3 -> Mangled name of the function where the entry was
12377 // identified.
12378 // - Entry 4 -> Line in the file where the entry was identified.
12379 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12380 // - Entry 6 -> Order the entry was created.
12381 // The first element of the metadata node is the kind.
12382 Metadata *Ops[] = {
12383 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12384 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12385 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12386 GetMDInt(E.getOrder())};
12387
12388 // Save this entry in the right position of the ordered entries array.
12389 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12390
12391 // Add metadata to the named metadata node.
12392 MD->addOperand(MDNode::get(C, Ops));
12393 };
12394
12395 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12396
12397 // Create function that emits metadata for each device global variable entry;
12398 auto &&DeviceGlobalVarMetadataEmitter =
12399 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12400 StringRef MangledName,
12402 // Generate metadata for global variables. Each entry of this metadata
12403 // contains:
12404 // - Entry 0 -> Kind of this type of metadata (1).
12405 // - Entry 1 -> Mangled name of the variable.
12406 // - Entry 2 -> Declare target kind.
12407 // - Entry 3 -> Order the entry was created.
12408 // The first element of the metadata node is the kind.
12409 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12410 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12411
12412 // Save this entry in the right position of the ordered entries array.
12413 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12414 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12415
12416 // Add metadata to the named metadata node.
12417 MD->addOperand(MDNode::get(C, Ops));
12418 };
12419
12420 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12421 DeviceGlobalVarMetadataEmitter);
12422
12423 for (const auto &E : OrderedEntries) {
12424 assert(E.first && "All ordered entries must exist!");
12425 if (const auto *CE =
12427 E.first)) {
12428 if (!CE->getID() || !CE->getAddress()) {
12429 // Do not blame the entry if the parent funtion is not emitted.
12430 TargetRegionEntryInfo EntryInfo = E.second;
12431 StringRef FnName = EntryInfo.ParentName;
12432 if (!M.getNamedValue(FnName))
12433 continue;
12434 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12435 continue;
12436 }
12437 createOffloadEntry(CE->getID(), CE->getAddress(),
12438 /*Size=*/0, CE->getFlags(),
12440 } else if (const auto *CE = dyn_cast<
12442 E.first)) {
12445 CE->getFlags());
12446 switch (Flags) {
12449 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12450 continue;
12451 if (!CE->getAddress()) {
12452 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12453 continue;
12454 }
12455 // The vaiable has no definition - no need to add the entry.
12456 if (CE->getVarSize() == 0)
12457 continue;
12458 break;
12460 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12461 (!Config.isTargetDevice() && CE->getAddress())) &&
12462 "Declaret target link address is set.");
12463 if (Config.isTargetDevice())
12464 continue;
12465 if (!CE->getAddress()) {
12467 continue;
12468 }
12469 break;
12472 if (!CE->getAddress()) {
12473 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12474 continue;
12475 }
12476 break;
12477 default:
12478 break;
12479 }
12480
12481 // Hidden or internal symbols on the device are not externally visible.
12482 // We should not attempt to register them by creating an offloading
12483 // entry. Indirect variables are handled separately on the device.
12484 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12485 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12486 (Flags !=
12488 Flags != OffloadEntriesInfoManager::
12489 OMPTargetGlobalVarEntryIndirectVTable))
12490 continue;
12491
12492 // Indirect globals need to use a special name that doesn't match the name
12493 // of the associated host global.
12495 Flags ==
12497 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12498 Flags, CE->getLinkage(), CE->getVarName());
12499 else
12500 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12501 Flags, CE->getLinkage());
12502
12503 } else {
12504 llvm_unreachable("Unsupported entry kind.");
12505 }
12506 }
12507
12508 // Emit requires directive globals to a special entry so the runtime can
12509 // register them when the device image is loaded.
12510 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12511 // entries should be redesigned to better suit this use-case.
12512 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12516 ".requires", /*Size=*/0,
12518 Config.getRequiresFlags());
12519}
12520
12523 unsigned FileID, unsigned Line, unsigned Count) {
12524 raw_svector_ostream OS(Name);
12525 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12526 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12527 if (Count)
12528 OS << "_" << Count;
12529}
12530
12532 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12533 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12535 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12536 EntryInfo.Line, NewCount);
12537}
12538
12541 vfs::FileSystem &VFS,
12542 StringRef ParentName) {
12543 sys::fs::UniqueID ID(0xdeadf17e, 0);
12544 auto FileIDInfo = CallBack();
12545 uint64_t FileID = 0;
12546 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12547 ID = Status->getUniqueID();
12548 FileID = Status->getUniqueID().getFile();
12549 } else {
12550 // If the inode ID could not be determined, create a hash value
12551 // the current file name and use that as an ID.
12552 FileID = hash_value(std::get<0>(FileIDInfo));
12553 }
12554
12555 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12556 std::get<1>(FileIDInfo));
12557}
12558
12560 unsigned Offset = 0;
12561 for (uint64_t Remain =
12562 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12564 !(Remain & 1); Remain = Remain >> 1)
12565 Offset++;
12566 return Offset;
12567}
12568
12571 // Rotate by getFlagMemberOffset() bits.
12572 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12573 << getFlagMemberOffset());
12574}
12575
12578 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12579 // If the entry is PTR_AND_OBJ but has not been marked with the special
12580 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12581 // marked as MEMBER_OF.
12582 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12584 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12587 return;
12588
12589 // Entries with ATTACH are not members-of anything. They are handled
12590 // separately by the runtime after other maps have been handled.
12591 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12593 return;
12594
12595 // Reset the placeholder value to prepare the flag for the assignment of the
12596 // proper MEMBER_OF value.
12597 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12598 Flags |= MemberOfFlag;
12599}
12600
12604 bool IsDeclaration, bool IsExternallyVisible,
12605 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12606 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12607 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12608 std::function<Constant *()> GlobalInitializer,
12609 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12610 // TODO: convert this to utilise the IRBuilder Config rather than
12611 // a passed down argument.
12612 if (OpenMPSIMD)
12613 return nullptr;
12614
12617 CaptureClause ==
12619 Config.hasRequiresUnifiedSharedMemory())) {
12620 SmallString<64> PtrName;
12621 {
12622 raw_svector_ostream OS(PtrName);
12623 OS << MangledName;
12624 if (!IsExternallyVisible)
12625 OS << format("_%x", EntryInfo.FileID);
12626 OS << "_decl_tgt_ref_ptr";
12627 }
12628
12629 Value *Ptr = M.getNamedValue(PtrName);
12630
12631 if (!Ptr) {
12632 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12633 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12634
12635 auto *GV = cast<GlobalVariable>(Ptr);
12636 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12637
12638 if (!Config.isTargetDevice()) {
12639 if (GlobalInitializer)
12640 GV->setInitializer(GlobalInitializer());
12641 else
12642 GV->setInitializer(GlobalValue);
12643 }
12644
12646 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12647 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12648 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12649 }
12650
12651 return cast<Constant>(Ptr);
12652 }
12653
12654 return nullptr;
12655}
12656
12660 bool IsDeclaration, bool IsExternallyVisible,
12661 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12662 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12663 std::vector<Triple> TargetTriple,
12664 std::function<Constant *()> GlobalInitializer,
12665 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12666 Constant *Addr) {
12668 (TargetTriple.empty() && !Config.isTargetDevice()))
12669 return;
12670
12672 StringRef VarName;
12673 int64_t VarSize;
12675
12677 CaptureClause ==
12679 !Config.hasRequiresUnifiedSharedMemory()) {
12681 VarName = MangledName;
12682 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12683
12684 if (!IsDeclaration)
12685 VarSize = divideCeil(
12686 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12687 else
12688 VarSize = 0;
12689 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12690
12691 // This is a workaround carried over from Clang which prevents undesired
12692 // optimisation of internal variables.
12693 if (Config.isTargetDevice() &&
12694 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12695 // Do not create a "ref-variable" if the original is not also available
12696 // on the host.
12697 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12698 return;
12699
12700 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12701
12702 if (!M.getNamedValue(RefName)) {
12703 Constant *AddrRef =
12704 getOrCreateInternalVariable(Addr->getType(), RefName);
12705 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12706 GvAddrRef->setConstant(true);
12707 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12708 GvAddrRef->setInitializer(Addr);
12709 GeneratedRefs.push_back(GvAddrRef);
12710 }
12711 }
12712 } else {
12715 else
12717
12718 if (Config.isTargetDevice()) {
12719 VarName = (Addr) ? Addr->getName() : "";
12720 Addr = nullptr;
12721 } else {
12723 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12724 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12725 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12726 VarName = (Addr) ? Addr->getName() : "";
12727 }
12728 VarSize = M.getDataLayout().getPointerSize();
12730 }
12731
12732 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12733 Flags, Linkage);
12734}
12735
12736/// Loads all the offload entries information from the host IR
12737/// metadata.
12739 // If we are in target mode, load the metadata from the host IR. This code has
12740 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12741
12742 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12743 if (!MD)
12744 return;
12745
12746 for (MDNode *MN : MD->operands()) {
12747 auto &&GetMDInt = [MN](unsigned Idx) {
12748 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12749 return cast<ConstantInt>(V->getValue())->getZExtValue();
12750 };
12751
12752 auto &&GetMDString = [MN](unsigned Idx) {
12753 auto *V = cast<MDString>(MN->getOperand(Idx));
12754 return V->getString();
12755 };
12756
12757 switch (GetMDInt(0)) {
12758 default:
12759 llvm_unreachable("Unexpected metadata!");
12760 break;
12761 case OffloadEntriesInfoManager::OffloadEntryInfo::
12762 OffloadingEntryInfoTargetRegion: {
12763 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12764 /*DeviceID=*/GetMDInt(1),
12765 /*FileID=*/GetMDInt(2),
12766 /*Line=*/GetMDInt(4),
12767 /*Count=*/GetMDInt(5));
12768 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12769 /*Order=*/GetMDInt(6));
12770 break;
12771 }
12772 case OffloadEntriesInfoManager::OffloadEntryInfo::
12773 OffloadingEntryInfoDeviceGlobalVar:
12774 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12775 /*MangledName=*/GetMDString(1),
12777 /*Flags=*/GetMDInt(2)),
12778 /*Order=*/GetMDInt(3));
12779 break;
12780 }
12781 }
12782}
12783
12785 StringRef HostFilePath) {
12786 if (HostFilePath.empty())
12787 return;
12788
12789 auto Buf = VFS.getBufferForFile(HostFilePath);
12790 if (std::error_code Err = Buf.getError()) {
12791 report_fatal_error(("error opening host file from host file path inside of "
12792 "OpenMPIRBuilder: " +
12793 Err.message())
12794 .c_str());
12795 }
12796
12797 LLVMContext Ctx;
12799 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12800 if (std::error_code Err = M.getError()) {
12802 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12803 .c_str());
12804 }
12805
12806 loadOffloadInfoMetadata(*M.get());
12807}
12808
12811 llvm::StringRef Name) {
12812 Builder.restoreIP(Loc.IP);
12813
12814 BasicBlock *CurBB = Builder.GetInsertBlock();
12815 assert(CurBB &&
12816 "expected a valid insertion block for creating an iterator loop");
12817 Function *F = CurBB->getParent();
12818
12819 InsertPointTy SplitIP = Builder.saveIP();
12820 if (SplitIP.getPoint() == CurBB->end())
12821 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12822 SplitIP = InsertPointTy(CurBB, Terminator->getIterator());
12823
12824 BasicBlock *ContBB =
12825 splitBB(SplitIP, /*CreateBranch=*/false,
12826 Builder.getCurrentDebugLocation(), "omp.it.cont");
12827
12828 CanonicalLoopInfo *CLI =
12829 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12830 /*PreInsertBefore=*/ContBB,
12831 /*PostInsertBefore=*/ContBB, Name);
12832
12833 // Enter loop from original block.
12834 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12835
12836 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12837 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12838 T->eraseFromParent();
12839
12840 InsertPointTy BodyIP = CLI->getBodyIP();
12841 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12842 return Err;
12843
12844 // Body must either fallthrough to the latch or branch directly to it.
12845 if (Instruction *BodyTerminator = CLI->getBody()->getTerminatorOrNull()) {
12846 auto *BodyBr = dyn_cast<UncondBrInst>(BodyTerminator);
12847 if (!BodyBr || BodyBr->getSuccessor() != CLI->getLatch()) {
12849 "iterator bodygen must terminate the canonical body with an "
12850 "unconditional branch to the loop latch",
12852 }
12853 } else {
12854 // Ensure we end the loop body by jumping to the latch.
12855 Builder.SetInsertPoint(CLI->getBody());
12856 Builder.CreateBr(CLI->getLatch());
12857 }
12858
12859 // Link After -> ContBB
12860 Builder.SetInsertPoint(CLI->getAfter(), CLI->getAfter()->begin());
12861 if (!CLI->getAfter()->hasTerminator())
12862 Builder.CreateBr(ContBB);
12863
12864 return InsertPointTy{ContBB, ContBB->begin()};
12865}
12866
12867/// Mangle the parameter part of the vector function name according to
12868/// their OpenMP classification. The mangling function is defined in
12869/// section 4.5 of the AAVFABI(2021Q1).
12870static std::string mangleVectorParameters(
12872 SmallString<256> Buffer;
12873 llvm::raw_svector_ostream Out(Buffer);
12874 for (const auto &ParamAttr : ParamAttrs) {
12875 switch (ParamAttr.Kind) {
12877 Out << 'l';
12878 break;
12880 Out << 'R';
12881 break;
12883 Out << 'U';
12884 break;
12886 Out << 'L';
12887 break;
12889 Out << 'u';
12890 break;
12892 Out << 'v';
12893 break;
12894 }
12895 if (ParamAttr.HasVarStride)
12896 Out << "s" << ParamAttr.StrideOrArg;
12897 else if (ParamAttr.Kind ==
12899 ParamAttr.Kind ==
12901 ParamAttr.Kind ==
12903 ParamAttr.Kind ==
12905 // Don't print the step value if it is not present or if it is
12906 // equal to 1.
12907 if (ParamAttr.StrideOrArg < 0)
12908 Out << 'n' << -ParamAttr.StrideOrArg;
12909 else if (ParamAttr.StrideOrArg != 1)
12910 Out << ParamAttr.StrideOrArg;
12911 }
12912
12913 if (!!ParamAttr.Alignment)
12914 Out << 'a' << ParamAttr.Alignment;
12915 }
12916
12917 return std::string(Out.str());
12918}
12919
12921 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
12923 struct ISADataTy {
12924 char ISA;
12925 unsigned VecRegSize;
12926 };
12927 ISADataTy ISAData[] = {
12928 {'b', 128}, // SSE
12929 {'c', 256}, // AVX
12930 {'d', 256}, // AVX2
12931 {'e', 512}, // AVX512
12932 };
12934 switch (Branch) {
12936 Masked.push_back('N');
12937 Masked.push_back('M');
12938 break;
12940 Masked.push_back('N');
12941 break;
12943 Masked.push_back('M');
12944 break;
12945 }
12946 for (char Mask : Masked) {
12947 for (const ISADataTy &Data : ISAData) {
12949 llvm::raw_svector_ostream Out(Buffer);
12950 Out << "_ZGV" << Data.ISA << Mask;
12951 if (!VLENVal) {
12952 assert(NumElts && "Non-zero simdlen/cdtsize expected");
12953 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
12954 } else {
12955 Out << VLENVal;
12956 }
12957 Out << mangleVectorParameters(ParamAttrs);
12958 Out << '_' << Fn->getName();
12959 Fn->addFnAttr(Out.str());
12960 }
12961 }
12962}
12963
12964// Function used to add the attribute. The parameter `VLEN` is templated to
12965// allow the use of `x` when targeting scalable functions for SVE.
12966template <typename T>
12967static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
12968 char ISA, StringRef ParSeq,
12969 StringRef MangledName, bool OutputBecomesInput,
12970 llvm::Function *Fn) {
12971 SmallString<256> Buffer;
12972 llvm::raw_svector_ostream Out(Buffer);
12973 Out << Prefix << ISA << LMask << VLEN;
12974 if (OutputBecomesInput)
12975 Out << 'v';
12976 Out << ParSeq << '_' << MangledName;
12977 Fn->addFnAttr(Out.str());
12978}
12979
12980// Helper function to generate the Advanced SIMD names depending on the value
12981// of the NDS when simdlen is not present.
12982static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
12983 StringRef Prefix, char ISA,
12984 StringRef ParSeq, StringRef MangledName,
12985 bool OutputBecomesInput,
12986 llvm::Function *Fn) {
12987 switch (NDS) {
12988 case 8:
12989 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12990 OutputBecomesInput, Fn);
12991 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
12992 OutputBecomesInput, Fn);
12993 break;
12994 case 16:
12995 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
12996 OutputBecomesInput, Fn);
12997 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12998 OutputBecomesInput, Fn);
12999 break;
13000 case 32:
13001 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13002 OutputBecomesInput, Fn);
13003 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13004 OutputBecomesInput, Fn);
13005 break;
13006 case 64:
13007 case 128:
13008 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13009 OutputBecomesInput, Fn);
13010 break;
13011 default:
13012 llvm_unreachable("Scalar type is too wide.");
13013 }
13014}
13015
13016/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
13018 llvm::Function *Fn, unsigned UserVLEN,
13020 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
13021 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
13022
13023 // Sort out parameter sequence.
13024 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
13025 StringRef Prefix = "_ZGV";
13026 StringRef MangledName = Fn->getName();
13027
13028 // Generate simdlen from user input (if any).
13029 if (UserVLEN) {
13030 if (ISA == 's') {
13031 // SVE generates only a masked function.
13032 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13033 OutputBecomesInput, Fn);
13034 return;
13035 }
13036
13037 switch (Branch) {
13039 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13040 OutputBecomesInput, Fn);
13041 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13042 OutputBecomesInput, Fn);
13043 break;
13045 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13046 OutputBecomesInput, Fn);
13047 break;
13049 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13050 OutputBecomesInput, Fn);
13051 break;
13052 }
13053 return;
13054 }
13055
13056 if (ISA == 's') {
13057 // SVE, section 3.4.1, item 1.
13058 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13059 OutputBecomesInput, Fn);
13060 return;
13061 }
13062
13063 switch (Branch) {
13065 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13066 MangledName, OutputBecomesInput, Fn);
13067 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13068 MangledName, OutputBecomesInput, Fn);
13069 break;
13071 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13072 MangledName, OutputBecomesInput, Fn);
13073 break;
13075 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13076 MangledName, OutputBecomesInput, Fn);
13077 break;
13078 }
13079}
13080
13081//===----------------------------------------------------------------------===//
13082// OffloadEntriesInfoManager
13083//===----------------------------------------------------------------------===//
13084
13086 return OffloadEntriesTargetRegion.empty() &&
13087 OffloadEntriesDeviceGlobalVar.empty();
13088}
13089
13090unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13091 const TargetRegionEntryInfo &EntryInfo) const {
13092 auto It = OffloadEntriesTargetRegionCount.find(
13093 getTargetRegionEntryCountKey(EntryInfo));
13094 if (It == OffloadEntriesTargetRegionCount.end())
13095 return 0;
13096 return It->second;
13097}
13098
13099void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13100 const TargetRegionEntryInfo &EntryInfo) {
13101 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13102 EntryInfo.Count + 1;
13103}
13104
13105/// Initialize target region entry.
13107 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13108 OffloadEntriesTargetRegion[EntryInfo] =
13109 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13111 ++OffloadingEntriesNum;
13112}
13113
13115 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13117 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13118
13119 // Update the EntryInfo with the next available count for this location.
13120 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13121
13122 // If we are emitting code for a target, the entry is already initialized,
13123 // only has to be registered.
13124 if (OMPBuilder->Config.isTargetDevice()) {
13125 // This could happen if the device compilation is invoked standalone.
13126 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13127 return;
13128 }
13129 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13130 Entry.setAddress(Addr);
13131 Entry.setID(ID);
13132 Entry.setFlags(Flags);
13133 } else {
13135 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13136 return;
13137 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13138 "Target region entry already registered!");
13139 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13140 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13141 ++OffloadingEntriesNum;
13142 }
13143 incrementTargetRegionEntryInfoCount(EntryInfo);
13144}
13145
13147 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13148
13149 // Update the EntryInfo with the next available count for this location.
13150 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13151
13152 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13153 if (It == OffloadEntriesTargetRegion.end()) {
13154 return false;
13155 }
13156 // Fail if this entry is already registered.
13157 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13158 return false;
13159 return true;
13160}
13161
13163 const OffloadTargetRegionEntryInfoActTy &Action) {
13164 // Scan all target region entries and perform the provided action.
13165 for (const auto &It : OffloadEntriesTargetRegion) {
13166 Action(It.first, It.second);
13167 }
13168}
13169
13171 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13172 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13173 ++OffloadingEntriesNum;
13174}
13175
13177 StringRef VarName, Constant *Addr, int64_t VarSize,
13179 if (OMPBuilder->Config.isTargetDevice()) {
13180 // This could happen if the device compilation is invoked standalone.
13181 if (!hasDeviceGlobalVarEntryInfo(VarName))
13182 return;
13183 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13184 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13185 if (Entry.getVarSize() == 0) {
13186 Entry.setVarSize(VarSize);
13187 Entry.setLinkage(Linkage);
13188 }
13189 return;
13190 }
13191 Entry.setVarSize(VarSize);
13192 Entry.setLinkage(Linkage);
13193 Entry.setAddress(Addr);
13194 } else {
13195 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13196 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13197 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13198 "Entry not initialized!");
13199 if (Entry.getVarSize() == 0) {
13200 Entry.setVarSize(VarSize);
13201 Entry.setLinkage(Linkage);
13202 }
13203 return;
13204 }
13206 Flags ==
13208 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13209 Addr, VarSize, Flags, Linkage,
13210 VarName.str());
13211 else
13212 OffloadEntriesDeviceGlobalVar.try_emplace(
13213 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13214 ++OffloadingEntriesNum;
13215 }
13216}
13217
13220 // Scan all target region entries and perform the provided action.
13221 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13222 Action(E.getKey(), E.getValue());
13223}
13224
13225//===----------------------------------------------------------------------===//
13226// CanonicalLoopInfo
13227//===----------------------------------------------------------------------===//
13228
13229void CanonicalLoopInfo::collectControlBlocks(
13231 // We only count those BBs as control block for which we do not need to
13232 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13233 // flow. For consistency, this also means we do not add the Body block, which
13234 // is just the entry to the body code.
13235 BBs.reserve(BBs.size() + 6);
13236 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13237}
13238
13240 assert(isValid() && "Requires a valid canonical loop");
13241 for (BasicBlock *Pred : predecessors(Header)) {
13242 if (Pred != Latch)
13243 return Pred;
13244 }
13245 llvm_unreachable("Missing preheader");
13246}
13247
13248void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13249 assert(isValid() && "Requires a valid canonical loop");
13250
13251 Instruction *CmpI = &getCond()->front();
13252 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13253 CmpI->setOperand(1, TripCount);
13254
13255#ifndef NDEBUG
13256 assertOK();
13257#endif
13258}
13259
13260void CanonicalLoopInfo::mapIndVar(
13261 llvm::function_ref<Value *(Instruction *)> Updater) {
13262 assert(isValid() && "Requires a valid canonical loop");
13263
13264 Instruction *OldIV = getIndVar();
13265
13266 // Record all uses excluding those introduced by the updater. Uses by the
13267 // CanonicalLoopInfo itself to keep track of the number of iterations are
13268 // excluded.
13269 SmallVector<Use *> ReplacableUses;
13270 for (Use &U : OldIV->uses()) {
13271 auto *User = dyn_cast<Instruction>(U.getUser());
13272 if (!User)
13273 continue;
13274 if (User->getParent() == getCond())
13275 continue;
13276 if (User->getParent() == getLatch())
13277 continue;
13278 ReplacableUses.push_back(&U);
13279 }
13280
13281 // Run the updater that may introduce new uses
13282 Value *NewIV = Updater(OldIV);
13283
13284 // Replace the old uses with the value returned by the updater.
13285 for (Use *U : ReplacableUses)
13286 U->set(NewIV);
13287
13288#ifndef NDEBUG
13289 assertOK();
13290#endif
13291}
13292
13294#ifndef NDEBUG
13295 // No constraints if this object currently does not describe a loop.
13296 if (!isValid())
13297 return;
13298
13299 BasicBlock *Preheader = getPreheader();
13300 BasicBlock *Body = getBody();
13301 BasicBlock *After = getAfter();
13302
13303 // Verify standard control-flow we use for OpenMP loops.
13304 assert(Preheader);
13305 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13306 "Preheader must terminate with unconditional branch");
13307 assert(Preheader->getSingleSuccessor() == Header &&
13308 "Preheader must jump to header");
13309
13310 assert(Header);
13311 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13312 "Header must terminate with unconditional branch");
13313 assert(Header->getSingleSuccessor() == Cond &&
13314 "Header must jump to exiting block");
13315
13316 assert(Cond);
13317 assert(Cond->getSinglePredecessor() == Header &&
13318 "Exiting block only reachable from header");
13319
13320 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13321 "Exiting block must terminate with conditional branch");
13322 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13323 "Exiting block's first successor jump to the body");
13324 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13325 "Exiting block's second successor must exit the loop");
13326
13327 assert(Body);
13328 assert(Body->getSinglePredecessor() == Cond &&
13329 "Body only reachable from exiting block");
13330 assert(!isa<PHINode>(Body->front()));
13331
13332 assert(Latch);
13333 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13334 "Latch must terminate with unconditional branch");
13335 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13336 // TODO: To support simple redirecting of the end of the body code that has
13337 // multiple; introduce another auxiliary basic block like preheader and after.
13338 assert(Latch->getSinglePredecessor() != nullptr);
13339 assert(!isa<PHINode>(Latch->front()));
13340
13341 assert(Exit);
13342 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13343 "Exit block must terminate with unconditional branch");
13344 assert(Exit->getSingleSuccessor() == After &&
13345 "Exit block must jump to after block");
13346
13347 assert(After);
13348 assert(After->getSinglePredecessor() == Exit &&
13349 "After block only reachable from exit block");
13350 assert(After->empty() || !isa<PHINode>(After->front()));
13351
13352 Instruction *IndVar = getIndVar();
13353 assert(IndVar && "Canonical induction variable not found?");
13354 assert(isa<IntegerType>(IndVar->getType()) &&
13355 "Induction variable must be an integer");
13356 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13357 "Induction variable must be a PHI in the loop header");
13358 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13359 assert(
13360 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13361 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13362
13363 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13364 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13365 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13366 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13367 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13368 ->isOne());
13369
13370 Value *TripCount = getTripCount();
13371 assert(TripCount && "Loop trip count not found?");
13372 assert(IndVar->getType() == TripCount->getType() &&
13373 "Trip count and induction variable must have the same type");
13374
13375 auto *CmpI = cast<CmpInst>(&Cond->front());
13376 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13377 "Exit condition must be a signed less-than comparison");
13378 assert(CmpI->getOperand(0) == IndVar &&
13379 "Exit condition must compare the induction variable");
13380 assert(CmpI->getOperand(1) == TripCount &&
13381 "Exit condition must compare with the trip count");
13382#endif
13383}
13384
13386 Header = nullptr;
13387 Cond = nullptr;
13388 Latch = nullptr;
13389 Exit = nullptr;
13390}
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:539
static cl::opt< unsigned > TileSize("fuse-matrix-tile-size", cl::init(4), cl::Hidden, cl::desc("Tile size for matrix instruction fusion using square-shaped tiles."))
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
This file contains the declarations for metadata subclasses.
#define T
uint64_t IntrinsicInst * II
#define OMP_KERNEL_ARG_VERSION
Provides definitions for Target specific Grid Values.
static Value * removeASCastIfPresent(Value *V)
static void createTargetLoopWorkshareCall(OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType, BasicBlock *InsertBlock, Value *Ident, Value *LoopBodyArg, Value *TripCount, Function &LoopBodyFn, bool NoLoop)
Value * createFakeIntVal(IRBuilderBase &Builder, OpenMPIRBuilder::InsertPointTy OuterAllocaIP, llvm::SmallVectorImpl< Instruction * > &ToBeDeleted, OpenMPIRBuilder::InsertPointTy InnerAllocaIP, const Twine &Name="", bool AsPtr=true, bool Is64Bit=false)
static Function * createTargetParallelWrapper(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn)
Create wrapper function used to gather the outlined function's argument structure from a shared buffe...
static void redirectTo(BasicBlock *Source, BasicBlock *Target, DebugLoc DL)
Make Source branch to Target.
static FunctionCallee getKmpcDistForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void applyParallelAccessesMetadata(CanonicalLoopInfo *CLI, LLVMContext &Ctx, Loop *Loop, LoopInfo &LoopInfo, SmallVector< Metadata * > &LoopMDList)
static 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:407
LLVM_ABI AttributeSet addAttributes(LLVMContext &C, AttributeSet AS) const
Add attributes to the attribute set.
LLVM_ABI AttributeSet addAttribute(LLVMContext &C, Attribute::AttrKind Kind) const
Add an argument attribute.
static LLVM_ABI Attribute getWithAlignment(LLVMContext &Context, Align Alignment)
Return a uniquified Attribute object that has the specific alignment set.
LLVM Basic Block Representation.
Definition BasicBlock.h:62
LLVM_ABI void replaceSuccessorsPhiUsesWith(BasicBlock *Old, BasicBlock *New)
Update all phi nodes in this basic block's successors to refer to basic block New instead of basic bl...
iterator end()
Definition BasicBlock.h:459
iterator begin()
Instruction iterator methods.
Definition BasicBlock.h:446
LLVM_ABI const_iterator getFirstInsertionPt() const
Returns an iterator to the first instruction in this block that is suitable for inserting a non-PHI i...
LLVM_ABI BasicBlock * splitBasicBlock(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction.
const Function * getParent() const
Return the enclosing method, or null if none.
Definition BasicBlock.h:213
reverse_iterator rbegin()
Definition BasicBlock.h:462
bool hasTerminator() const LLVM_READONLY
Returns whether the block has a terminator.
Definition BasicBlock.h:232
bool empty() const
Definition BasicBlock.h:468
const Instruction & back() const
Definition BasicBlock.h:471
LLVM_ABI BasicBlock * splitBasicBlockBefore(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction and insert the new basic blo...
LLVM_ABI InstListType::const_iterator getFirstNonPHIIt() const
Returns an iterator to the first instruction in this block that is not a PHINode instruction.
LLVM_ABI void insertDbgRecordBefore(DbgRecord *DR, InstListType::iterator Here)
Insert a DbgRecord into a block at the position given by Here.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
LLVM_ABI InstListType::const_iterator getFirstNonPHIOrDbg(bool SkipPseudoOp=true) const
Returns a pointer to the first instruction in this block that is not a PHINode or a debug intrinsic,...
LLVM_ABI const BasicBlock * getUniqueSuccessor() const
Return the successor of this block if it has a unique successor.
LLVM_ABI const BasicBlock * getSinglePredecessor() const
Return the predecessor of this block if it has a single predecessor block.
const Instruction & front() const
Definition BasicBlock.h:469
InstListType::reverse_iterator reverse_iterator
Definition BasicBlock.h:172
LLVM_ABI const BasicBlock * getUniquePredecessor() const
Return the predecessor of this block if it has a unique predecessor block.
const Instruction * getTerminatorOrNull() const LLVM_READONLY
Returns the terminator instruction if the block is well formed or null if the block is not well forme...
Definition BasicBlock.h:248
LLVM_ABI const BasicBlock * getSingleSuccessor() const
Return the successor of this block if it has a single successor.
LLVM_ABI SymbolTableList< BasicBlock >::iterator eraseFromParent()
Unlink 'this' from the containing function and delete it.
reverse_iterator rend()
Definition BasicBlock.h:464
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
LLVM_ABI LLVMContext & getContext() const
Get the context in which this basic block lives.
void moveBefore(BasicBlock *MovePos)
Unlink this basic block from its current function and insert it into the function that MovePos lives ...
Definition BasicBlock.h:373
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction; assumes that the block is well-formed.
Definition BasicBlock.h:237
void splice(BasicBlock::iterator ToIt, BasicBlock *FromBB)
Transfer all instructions from FromBB to this basic block at ToIt.
Definition BasicBlock.h:644
LLVM_ABI void removePredecessor(BasicBlock *Pred, bool KeepOneInputPHIs=false)
Update PHI nodes in this BasicBlock before removal of predecessor Pred.
void setDoesNotThrow()
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
Value * getArgOperand(unsigned i) const
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
unsigned arg_size() const
This class represents a function call, abstracting a target machine's calling convention.
Class to represented the control flow structure of an OpenMP canonical loop.
Value * getTripCount() const
Returns the llvm::Value containing the number of loop iterations.
BasicBlock * getHeader() const
The header is the entry for each iteration.
LLVM_ABI void assertOK() const
Consistency self-check.
Type * getIndVarType() const
Return the type of the induction variable (and the trip count).
BasicBlock * getBody() const
The body block is the single entry for a loop iteration and not controlled by CanonicalLoopInfo.
bool isValid() const
Returns whether this object currently represents the IR of a loop.
void setLastIter(Value *IterVar)
Sets the last iteration variable for this loop.
OpenMPIRBuilder::InsertPointTy getAfterIP() const
Return the insertion point for user code after the loop.
OpenMPIRBuilder::InsertPointTy getBodyIP() const
Return the insertion point for user code in the body.
BasicBlock * getAfter() const
The after block is intended for clean-up code such as lifetime end markers.
Function * getFunction() const
LLVM_ABI void invalidate()
Invalidate this loop.
BasicBlock * getLatch() const
Reaching the latch indicates the end of the loop body code.
OpenMPIRBuilder::InsertPointTy getPreheaderIP() const
Return the insertion point for user code before the loop.
BasicBlock * getCond() const
The condition block computes whether there is another loop iteration.
BasicBlock * getExit() const
Reaching the exit indicates no more iterations are being executed.
LLVM_ABI BasicBlock * getPreheader() const
The preheader ensures that there is only a single edge entering the loop.
Instruction * getIndVar() const
Returns the instruction representing the current logical induction variable.
Predicate
This enumeration lists the possible predicates for CmpInst subclasses.
Definition InstrTypes.h:740
@ ICMP_SLT
signed less than
Definition InstrTypes.h:769
@ ICMP_SLE
signed less or equal
Definition InstrTypes.h:770
@ FCMP_OLT
0 1 0 0 True if ordered and less than
Definition InstrTypes.h:746
@ FCMP_OGT
0 0 1 0 True if ordered and greater than
Definition InstrTypes.h:744
@ ICMP_UGT
unsigned greater than
Definition InstrTypes.h:763
@ ICMP_SGT
signed greater than
Definition InstrTypes.h:767
@ ICMP_ULT
unsigned less than
Definition InstrTypes.h:765
@ ICMP_NE
not equal
Definition InstrTypes.h:762
@ ICMP_ULE
unsigned less or equal
Definition InstrTypes.h:766
A cache for the CodeExtractor analysis.
Utility class for extracting code into a new function.
static LLVM_ABI Constant * get(ArrayType *T, ArrayRef< Constant * > V)
static ConstantAsMetadata * get(Constant *C)
Definition Metadata.h:537
static Constant * get(LLVMContext &Context, ArrayRef< ElementTy > Elts)
get() constructor - Return a constant with array type with an element count and element type matching...
Definition Constants.h:878
static LLVM_ABI Constant * getString(LLVMContext &Context, StringRef Initializer, bool AddNull=true, bool ByteString=false)
This method constructs a CDS and initializes it with a text string.
static LLVM_ABI Constant * getPointerCast(Constant *C, Type *Ty)
Create a BitCast, AddrSpaceCast, or a PtrToInt cast constant expression.
static LLVM_ABI Constant * getTruncOrBitCast(Constant *C, Type *Ty)
static LLVM_ABI Constant * getPointerBitCastOrAddrSpaceCast(Constant *C, Type *Ty)
Create a BitCast or AddrSpaceCast for a pointer type depending on the address space.
static LLVM_ABI Constant * getSizeOf(Type *Ty)
getSizeOf constant expr - computes the (alloc) size of a type (in address-units, not bits) in a targe...
static LLVM_ABI Constant * getAddrSpaceCast(Constant *C, Type *Ty, bool OnlyIfReduced=false)
static LLVM_ABI ConstantFP * getZero(Type *Ty, bool Negative=false)
This is the shared class of boolean and integer constants.
Definition Constants.h:87
static ConstantInt * getSigned(IntegerType *Ty, int64_t V, bool ImplicitTrunc=false)
Return a ConstantInt with the specified value for the specified type.
Definition Constants.h:135
static LLVM_ABI ConstantPointerNull * get(PointerType *T)
Static factory methods - Return objects of the specified value.
static LLVM_ABI Constant * get(StructType *T, ArrayRef< Constant * > V)
This is an important base class in LLVM.
Definition Constant.h:43
static LLVM_ABI Constant * getAllOnesValue(Type *Ty)
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
DILocalScope * getScope() const
Get the local scope for this variable.
DINodeArray getAnnotations() const
DIFile * getFile() const
Subprogram description. Uses SubclassData1.
Base class for types.
uint32_t getAlignInBits() const
DIFile * getFile() const
DIType * getType() const
unsigned getLine() const
StringRef getName() const
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
TypeSize getTypeStoreSize(Type *Ty) const
Returns the maximum number of bytes that may be overwritten by storing the specified type.
Definition DataLayout.h:579
Record of a variable value-assignment, aka a non instruction representation of the dbg....
A debug info location.
Definition DebugLoc.h:126
Analysis pass which computes a DominatorTree.
Definition Dominators.h:241
LLVM_ABI DominatorTree run(Function &F, FunctionAnalysisManager &)
Run the analysis pass over a function and produce a dominator tree.
bool properlyDominates(const DomTreeNodeBase< NodeT > *A, const DomTreeNodeBase< NodeT > *B) const
properlyDominates - Returns true iff A dominates B and A != B.
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Definition Dominators.h:122
Represents either an error or a value T.
Definition ErrorOr.h:56
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
reference get()
Returns a reference to the stored T value.
Definition Error.h:582
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
Class to represent function types.
Type * getParamType(unsigned i) const
Parameter type accessors.
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
void addFnAttr(Attribute::AttrKind Kind)
Add function attributes to this function.
Definition Function.cpp: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:2910
LLVM_ABI const DebugLoc & getStableDebugLoc() const
Fetch the debug location for this node, unless this is a debug intrinsic, in which case fetch the deb...
LLVM_ABI void removeFromParent()
This method unlinks 'this' from the containing basic block, but does not delete it.
LLVM_ABI unsigned getNumSuccessors() const LLVM_READONLY
Return the number of successors that this instruction has.
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
LLVM_ABI const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
LLVM_ABI void moveBefore(InstListType::iterator InsertPos)
Unlink this instruction from its current basic block and insert it into the basic block that MovePos ...
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
MDNode * getMetadata(unsigned KindID) const
Get the metadata of given kind attached to this Instruction.
LLVM_ABI BasicBlock * getSuccessor(unsigned Idx) const LLVM_READONLY
Return the specified successor. This instruction must be a terminator.
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set the metadata of the specified kind to the specified node.
LLVM_ABI void moveBeforePreserving(InstListType::iterator MovePos)
Perform a moveBefore operation, while signalling that the caller intends to preserve the original ord...
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
LLVM_ABI void insertAfter(Instruction *InsertPos)
Insert an unlinked instruction into a basic block immediately after the specified instruction.
Class to represent integer types.
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:348
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
An instruction for reading from memory.
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this load instruction.
Align getAlign() const
Return the alignment of the access that is being performed.
Analysis pass that exposes the LoopInfo for a function.
Definition LoopInfo.h:594
LLVM_ABI LoopInfo run(Function &F, FunctionAnalysisManager &AM)
ArrayRef< BlockT * > getBlocks() const
Get a list of the basic blocks which make up this loop.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
This class represents a loop nest and can be used to query its properties.
Represents a single loop in the control flow graph.
Definition LoopInfo.h:40
LLVM_ABI MDNode * createCallbackEncoding(unsigned CalleeArgNo, ArrayRef< int > Arguments, bool VarArgsArePassed)
Return metadata describing a callback (see llvm::AbstractCallSite).
Metadata node.
Definition Metadata.h:1069
LLVM_ABI void replaceOperandWith(unsigned I, Metadata *New)
Replace a specific operand.
static MDTuple * getDistinct(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1575
ArrayRef< MDOperand > operands() const
Definition Metadata.h:1424
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1567
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Definition Metadata.cpp:615
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:38
size_type size() const
Definition MapVector.h:58
Root of the metadata hierarchy.
Definition Metadata.h:64
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h: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
LLVM_ABI InsertPointOrErrorTy createTask(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB, bool Tied=true, Value *Final=nullptr, Value *IfCondition=nullptr, const DependenciesInfo &Dependencies={}, const AffinityData &Affinities={}, bool Mergeable=false, Value *EventHandle=nullptr, Value *Priority=nullptr)
Generator for #omp taskloop
function_ref< Expected< Function * >(unsigned int)> CustomMapperCallbackTy
LLVM_ABI InsertPointTy createOrderedDepend(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumLoops, ArrayRef< llvm::Value * > StoreValues, const Twine &Name, bool IsDependSource)
Generator for 'omp ordered depend (source | sink)'.
LLVM_ABI InsertPointTy createCopyinClauseBlocks(InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr, llvm::IntegerType *IntPtrTy, bool BranchtoEnd=true)
Generate conditional branch and relevant BasicBlocks through which private threads copy the 'copyin' ...
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &Original, Value &Inner, Value *&ReplVal)> PrivatizeCallbackTy
Callback type for variable privatization (think copy & default constructor).
LLVM_ABI bool isFinalized()
Check whether the finalize function has already run.
SmallVector< FinalizationInfo, 8 > FinalizationStack
The finalization stack made up of finalize callbacks currently in-flight, wrapped into FinalizationIn...
LLVM_ABI std::vector< CanonicalLoopInfo * > tileLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops, ArrayRef< Value * > TileSizes)
Tile a loop nest.
LLVM_ABI CallInst * createOMPInteropInit(const LocationDescription &Loc, Value *InteropVar, omp::OMPInteropType InteropType, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_init.
LLVM_ABI Error emitIfClause(Value *Cond, BodyGenCallbackTy ThenGen, BodyGenCallbackTy ElseGen, InsertPointTy AllocaIP={}, ArrayRef< BasicBlock * > DeallocBlocks={})
Emits code for OpenMP 'if' clause using specified BodyGenCallbackTy Here is the logic: if (Cond) { Th...
LLVM_ABI void finalize(Function *Fn=nullptr)
Finalize the underlying module, e.g., by outlining regions.
LLVM_ABI Function * getOrCreateRuntimeFunctionPtr(omp::RuntimeFunction FnID)
void addOutlineInfo(std::unique_ptr< OutlineInfo > &&OI)
Add a new region that will be outlined later.
LLVM_ABI InsertPointTy createTargetInit(const LocationDescription &Loc, const llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs &Attrs)
The omp target interface.
LLVM_ABI InsertPointOrErrorTy createReductions(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< ReductionInfo > ReductionInfos, ArrayRef< bool > IsByRef, bool IsNoWait=false, bool IsTeamsReduction=false)
Generator for 'omp reduction'.
const Triple T
The target triple of the underlying module.
DenseMap< std::pair< Constant *, uint64_t >, Constant * > IdentMap
Map to remember existing ident_t*.
LLVM_ABI CallInst * createOMPFree(const LocationDescription &Loc, Value *Addr, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_free.
LLVM_ABI InsertPointOrErrorTy createReductionsGPU(const LocationDescription &Loc, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< ReductionInfo > ReductionInfos, ArrayRef< bool > IsByRef, bool IsNoWait=false, bool IsTeamsReduction=false, bool IsSPMD=false, ReductionGenCBKind ReductionGenCBKind=ReductionGenCBKind::MLIR, std::optional< omp::GV > GridValue={}, Value *SrcLocInfo=nullptr)
Design of OpenMP reductions on the GPU.
LLVM_ABI FunctionCallee createForStaticInitFunction(unsigned IVSize, bool IVSigned, bool IsGPUDistribute)
Returns __kmpc_for_static_init_* runtime function for the specified size IVSize and sign IVSigned.
LLVM_ABI CallInst * createOMPAlloc(const LocationDescription &Loc, Value *Size, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_alloc.
LLVM_ABI void emitNonContiguousDescriptor(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo, TargetDataInfo &Info)
Emit an array of struct descriptors to be assigned to the offload args.
LLVM_ABI InsertPointOrErrorTy createSection(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB)
Generator for 'omp section'.
LLVM_ABI InsertPointOrErrorTy createTaskgroup(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB)
Generator for the taskgroup construct.
LLVM_ABI InsertPointOrErrorTy createParallel(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB, PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition, Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable)
Generator for 'omp parallel'.
function_ref< InsertPointOrErrorTy(InsertPointTy)> EmitFallbackCallbackTy
Callback function type for functions emitting the host fallback code that is executed when the kernel...
static LLVM_ABI TargetRegionEntryInfo getTargetEntryUniqueInfo(FileIdentifierInfoCallbackTy CallBack, vfs::FileSystem &VFS, StringRef ParentName="")
Creates a unique info for a target entry when provided a filename and line number from.
LLVM_ABI void emitTaskDependency(IRBuilderBase &Builder, Value *Entry, const DependData &Dep)
Store one kmp_depend_info entry at the given Entry pointer.
LLVM_ABI void emitBlock(BasicBlock *BB, Function *CurFn, bool IsFinished=false)
LLVM_ABI Value * getOrCreateThreadID(Value *Ident)
Return the current thread ID.
LLVM_ABI InsertPointOrErrorTy createMaster(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB)
Generator for 'omp master'.
LLVM_ABI InsertPointOrErrorTy 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.
std::forward_list< ScanInfo > ScanInfos
Collection of owned ScanInfo objects that eventually need to be free'd.
static LLVM_ABI void writeTeamsForKernel(const Triple &T, Function &Kernel, int32_t LB, int32_t UB)
LLVM_ABI Value * calculateCanonicalLoopTripCount(const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, const Twine &Name="loop")
Calculate the trip count of a canonical loop.
LLVM_ABI InsertPointOrErrorTy createBarrier(const LocationDescription &Loc, omp::Directive Kind, bool ForceSimpleCall=false, bool CheckCancelFlag=true)
Emitter methods for OpenMP directives.
LLVM_ABI void setCorrectMemberOfFlag(omp::OpenMPOffloadMappingFlags &Flags, omp::OpenMPOffloadMappingFlags MemberOfFlag)
Given an initial flag set, this function modifies it to contain the passed in MemberOfFlag generated ...
LLVM_ABI Error emitOffloadingArraysAndArgs(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info, TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo, CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous=false, bool ForEndCall=false, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr)
Allocates memory for and populates the arrays required for offloading (offload_{baseptrs|ptrs|mappers...
LLVM_ABI Constant * getOrCreateDefaultSrcLocStr(uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the default source location.
LLVM_ABI InsertPointOrErrorTy createCritical(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst)
Generator for 'omp critical'.
LLVM_ABI void createError(const LocationDescription &Loc, bool IsFatal, Value *Message)
Generate a call to the runtime to emit the diagnostic of an OpenMP error directive with at(execution)...
LLVM_ABI void createOffloadEntry(Constant *ID, Constant *Addr, uint64_t Size, int32_t Flags, GlobalValue::LinkageTypes, StringRef Name="")
Creates offloading entry for the provided entry ID ID, address Addr, size Size, and flags Flags.
static LLVM_ABI unsigned getOpenMPDefaultSimdAlign(const Triple &TargetTriple, const StringMap< bool > &Features)
Get the default alignment value for given target.
LLVM_ABI unsigned getFlagMemberOffset()
Get the offset of the OMP_MAP_MEMBER_OF field.
LLVM_ABI InsertPointOrErrorTy applyWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP, bool NeedsBarrier, llvm::omp::ScheduleKind SchedKind=llvm::omp::OMP_SCHEDULE_Default, Value *ChunkSize=nullptr, bool HasSimdModifier=false, bool HasMonotonicModifier=false, bool HasNonmonotonicModifier=false, bool HasOrderedClause=false, omp::WorksharingLoopType LoopType=omp::WorksharingLoopType::ForStaticLoop, bool NoLoop=false, bool HasDistSchedule=false, Value *DistScheduleChunkSize=nullptr)
Modifies the canonical loop to be a workshare loop.
LLVM_ABI InsertPointOrErrorTy createAtomicCapture(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, AtomicOpValue &V, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: — Only Scalar data types V = X; X = X BinOp Expr ,...
LLVM_ABI CanonicalLoopInfo * createLoopSkeleton(DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore, BasicBlock *PostInsertBefore, const Twine &Name={}, bool IsCollapsed=false)
Create the control flow structure of a canonical OpenMP loop.
LLVM_ABI void createOffloadEntriesAndInfoMetadata(EmitMetadataErrorReportFunctionTy &ErrorReportFunction)
LLVM_ABI void applySimd(CanonicalLoopInfo *Loop, MapVector< Value *, Value * > AlignedVars, Value *IfCond, omp::OrderKind Order, ConstantInt *Simdlen, ConstantInt *Safelen)
Add metadata to simd-ize a loop.
SmallVector< std::unique_ptr< OutlineInfo >, 16 > OutlineInfos
Collection of regions that need to be outlined during finalization.
LLVM_ABI InsertPointOrErrorTy createAtomicUpdate(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: X = X BinOp Expr ,or X = Expr BinOp X For complex Operations: X = ...
std::function< std::tuple< std::string, uint64_t >()> FileIdentifierInfoCallbackTy
bool isLastFinalizationInfoCancellable(omp::Directive DK)
Return true if the last entry in the finalization stack is of kind DK and cancellable.
LLVM_ABI InsertPointTy emitTargetKernel(const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return, Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads, Value *HostPtr, ArrayRef< Value * > KernelArgs)
Generate a target region entry call.
LLVM_ABI GlobalVariable * createOffloadMaptypes(SmallVectorImpl< uint64_t > &Mappings, std::string VarName)
Create the global variable holding the offload mappings information.
LLVM_ABI Expected< Function * > emitUserDefinedMapper(function_ref< MapInfosOrErrorTy(InsertPointTy CodeGenIP, llvm::Value *PtrPHI, llvm::Value *BeginArg)> PrivAndGenMapInfoCB, llvm::Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB, bool PreserveMemberOfFlags=false, bool PropagatePresentToPointee=false)
Emit the user-defined mapper function.
LLVM_ABI CallInst * createCachedThreadPrivate(const LocationDescription &Loc, llvm::Value *Pointer, llvm::ConstantInt *Size, const llvm::Twine &Name=Twine(""))
Create a runtime call for kmpc_threadprivate_cached.
IRBuilder Builder
The LLVM-IR Builder used to create IR.
LLVM_ABI GlobalValue * createGlobalFlag(unsigned Value, StringRef Name)
Create a hidden global flag Name in the module with initial value Value.
LLVM_ABI void emitOffloadingArraysArgument(IRBuilderBase &Builder, OpenMPIRBuilder::TargetDataRTArgs &RTArgs, OpenMPIRBuilder::TargetDataInfo &Info, bool ForEndCall=false)
Emit the arguments to be passed to the runtime library based on the arrays of base pointers,...
LLVM_ABI InsertPointOrErrorTy createMasked(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, Value *Filter)
Generator for 'omp masked'.
LLVM_ABI Expected< CanonicalLoopInfo * > createCanonicalLoop(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *TripCount, const Twine &Name="loop")
Generator for the control flow structure of an OpenMP canonical loop.
function_ref< Expected< InsertPointTy >( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value *DestPtr, Value *SrcPtr)> TaskDupCallbackTy
Callback type for task duplication function code generation.
LLVM_ABI Value * getSizeInBytes(Value *BasePtr)
Computes the size of type in bytes.
llvm::function_ref< llvm::Error( InsertPointTy BodyIP, llvm::Value *LinearIV)> IteratorBodyGenTy
LLVM_ABI FunctionCallee createDispatchDeinitFunction()
Returns __kmpc_dispatch_deinit runtime function.
LLVM_ABI void registerTargetGlobalVariable(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy, Constant *Addr)
Registers a target variable for device or host.
LLVM_ABI void createTargetDeinit(const LocationDescription &Loc, int32_t TeamsReductionDataSize=0)
Create a runtime call for kmpc_target_deinit.
BodyGenTy
Type of BodyGen to use for region codegen.
LLVM_ABI CanonicalLoopInfo * fuseLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops)
Fuse a sequence of loops.
LLVM_ABI void emitX86DeclareSimdFunction(llvm::Function *Fn, unsigned NumElements, const llvm::APSInt &VLENVal, llvm::ArrayRef< DeclareSimdAttrTy > ParamAttrs, DeclareSimdBranch Branch)
Emit x86 vector-function ABI attributes for a declare simd function.
SmallVector< llvm::Function *, 16 > ConstantAllocaRaiseCandidates
A collection of candidate target functions that's constant allocas will attempt to be raised on a cal...
OffloadEntriesInfoManager OffloadInfoManager
Info manager to keep track of target regions.
static LLVM_ABI std::pair< int32_t, int32_t > readTeamBoundsForKernel(const Triple &T, Function &Kernel)
Read/write a bounds on teams for Kernel.
const std::string ompOffloadInfoName
OMP Offload Info Metadata name string.
Expected< InsertPointTy > InsertPointOrErrorTy
Type used to represent an insertion point or an error value.
LLVM_ABI InsertPointTy createCopyPrivate(const LocationDescription &Loc, llvm::Value *BufSize, llvm::Value *CpyBuf, llvm::Value *CpyFn, llvm::Value *DidIt)
Generator for __kmpc_copyprivate.
LLVM_ABI InsertPointOrErrorTy createSections(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< StorableBodyGenCallbackTy > SectionCBs, PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait)
Generator for 'omp sections'.
std::function< void(EmitMetadataErrorKind, TargetRegionEntryInfo)> EmitMetadataErrorReportFunctionTy
Callback function type.
function_ref< InsertPointOrErrorTy( Argument &Arg, Value *Input, Value *&RetVal, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< InsertPointTy > DeallocIPs)> TargetGenArgAccessorsCallbackTy
LLVM_ABI Expected< ScanInfo * > scanInfoInitialize()
Creates a ScanInfo object, allocates and returns the pointer.
LLVM_ABI InsertPointOrErrorTy emitTargetTask(TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc, OpenMPIRBuilder::InsertPointTy AllocaIP, const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs, bool HasNoWait)
Generate a target-task for the target construct.
LLVM_ABI InsertPointTy createAtomicRead(const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V, AtomicOrdering AO, InsertPointTy AllocaIP)
Emit atomic Read for : V = X — Only Scalar data types.
function_ref< Error(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> BodyGenCallbackTy
Callback type for body (=inner region) code generation.
bool updateToLocation(const LocationDescription &Loc)
Update the internal location to Loc.
LLVM_ABI void createFlush(const LocationDescription &Loc)
Generator for 'omp flush'.
LLVM_ABI void createTaskwait(const LocationDescription &Loc, DependenciesInfo Dependencies={})
Generator for 'omp taskwait'.
LLVM_ABI Constant * getAddrOfDeclareTargetVar(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, Type *LlvmPtrTy, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage)
Retrieve (or create if non-existent) the address of a declare target variable, used in conjunction wi...
origPtr *with the address space normalization required by the runtime entry point *The NULL descriptor makes the runtime walk the enclosing taskgroups to *find the matching task_reduction registration for the item The lookups *are emitted at p Loc
EmitMetadataErrorKind
The kind of errors that can occur when emitting the offload entries and metadata.
unsigned getOpcode() const
Return the opcode for this Instruction or ConstantExpr.
Definition Operator.h:43
The optimization diagnostic interface.
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
Pseudo-analysis pass that exposes the PassInstrumentation to pass managers.
Class to represent pointers.
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
static LLVM_ABI PointerType * get(LLVMContext &C, unsigned AddressSpace)
This constructs an opaque pointer to an object in a numbered address space.
Definition Type.cpp:911
PostDominatorTree Class - Concrete subclass of DominatorTree that is used to compute the post-dominat...
Analysis pass that exposes the ScalarEvolution for a function.
LLVM_ABI ScalarEvolution run(Function &F, FunctionAnalysisManager &AM)
The main scalar evolution driver.
ScanInfo holds the information to assist in lowering of Scan reduction.
llvm::SmallDenseMap< llvm::Value *, llvm::Value * > * ScanBuffPtrs
Maps the private reduction variable to the pointer of the temporary buffer.
llvm::BasicBlock * OMPScanLoopExit
Exit block of loop body.
llvm::Value * IV
Keeps track of value of iteration variable for input/scan loop to be used for Scan directive lowering...
llvm::BasicBlock * OMPAfterScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanInit
Block before loop body where scan initializations are done.
llvm::BasicBlock * OMPBeforeScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanFinish
Block after loop body where scan finalizations are done.
llvm::Value * Span
Stores the span of canonical loop being lowered to be used for temporary buffer allocation or Finaliz...
bool OMPFirstScanLoop
If true, it indicates Input phase is lowered; else it indicates ScanPhase is lowered.
llvm::BasicBlock * OMPScanDispatch
Controls the flow to before or after scan blocks.
A vector that has set insertion semantics.
Definition SetVector.h:57
bool remove_if(UnaryPredicate P)
Remove items from the set vector based on a predicate function.
Definition SetVector.h:236
bool empty() const
Determine if the SetVector is empty or not.
Definition SetVector.h:100
This is a 'bitvector' (really, a variable-sized bit array), optimized for the case when the array is ...
SmallBitVector & set()
bool test(unsigned Idx) const
Returns true if bit Idx is set.
bool all() const
Returns true if all bits are set.
bool any() const
Returns true if any bit is set.
A templated base class for SmallPtrSet which provides the typesafe interface that is common across al...
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition SmallSet.h:134
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
void append(StringRef RHS)
Append from a StringRef.
Definition SmallString.h:68
StringRef str() const
Explicit conversion to StringRef.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
void reserve(size_type N)
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void resize(size_type N)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
void setAlignment(Align Align)
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this store instruction.
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h: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:477
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:683
Type * getElementType(unsigned N) const
Multiway switch.
LLVM_ABI void addCase(ConstantInt *OnVal, BasicBlock *Dest)
Add an entry to the switch instruction.
Analysis pass providing the TargetTransformInfo.
LLVM_ABI Result run(const Function &F, FunctionAnalysisManager &)
Analysis pass providing the TargetLibraryInfo.
Target - Wrapper for Target specific information.
TargetMachine * createTargetMachine(const Triple &TT, StringRef CPU, StringRef Features, const TargetOptions &Options, std::optional< Reloc::Model > RM, std::optional< CodeModel::Model > CM=std::nullopt, CodeGenOptLevel OL=CodeGenOptLevel::Default, bool JIT=false) const
createTargetMachine - Create a target specific machine implementation for the specified Triple.
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
bool isPPC() const
Tests whether the target is PowerPC (32- or 64-bit LE or BE).
Definition Triple.h:1136
bool isX86() const
Tests whether the target is x86 (32- or 64-bit).
Definition Triple.h:1196
bool isWasm() const
Tests whether the target is wasm (32- and 64-bit).
Definition Triple.h:1210
bool isSystemZ() const
Tests whether the target is SystemZ.
Definition Triple.h:1193
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
LLVM_ABI std::string str() const
Return the twine contents as a std::string.
Definition Twine.cpp:17
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt64Ty(LLVMContext &C)
Definition Type.cpp:310
LLVM_ABI unsigned getIntegerBitWidth() const
LLVM_ABI Type * getStructElementType(unsigned N) const
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:309
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:282
LLVM_ABI unsigned getPointerAddressSpace() const
Get the address space of this pointer or pointer vector type.
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:282
bool isStructTy() const
True if this is an instance of StructType.
Definition Type.h:276
LLVM_ABI unsigned getScalarSizeInBits() const LLVM_READONLY
If this is a vector type, return the getPrimitiveSizeInBits value for the element type.
Definition Type.cpp:232
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition Type.h:186
bool isIntegerTy() const
True if this is an instance of IntegerType.
Definition Type.h:257
static LLVM_ABI IntegerType * getIntNTy(LLVMContext &C, unsigned N)
Definition Type.cpp:313
bool isVoidTy() const
Return true if this is 'void'.
Definition Type.h:141
Unconditional Branch instruction.
static UncondBrInst * Create(BasicBlock *Target, InsertPosition InsertBefore=nullptr)
static LLVM_ABI UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
This function has undefined behavior.
Produce an estimate of the unrolled cost of the specified loop.
Definition UnrollLoop.h:150
LLVM_ABI bool canUnroll(OptimizationRemarkEmitter *ORE=nullptr, const Loop *L=nullptr) const
Whether it is legal to unroll this loop.
uint64_t getRolledLoopSize() const
Definition UnrollLoop.h:174
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
void setOperand(unsigned i, Value *Val)
Definition User.h:212
Value * getOperand(unsigned i) const
Definition User.h:207
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
user_iterator user_begin()
Definition Value.h:402
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
Definition Value.cpp:394
bool hasOneUse() const
Return true if there is exactly one use of this value.
Definition Value.h:439
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
iterator_range< user_iterator > users()
Definition Value.h:426
User * user_back()
Definition Value.h:412
LLVM_ABI Align getPointerAlignment(const DataLayout &DL) const
Returns an alignment of the pointer value.
Definition Value.cpp:1002
LLVM_ABI bool hasNUses(unsigned N) const
Return true if this Value has exactly N uses.
Definition Value.cpp:147
LLVM_ABI User * getUniqueUndroppableUser()
Return true if there is exactly one unique user of this value that cannot be dropped (that user can h...
Definition Value.cpp:185
LLVM_ABI const Value * stripPointerCasts() const
Strip off pointer casts, all-zero GEPs and address space casts.
Definition Value.cpp:713
bool use_empty() const
Definition Value.h:346
user_iterator user_end()
Definition Value.h:410
LLVM_ABI bool replaceUsesWithIf(Value *New, llvm::function_ref< bool(Use &U)> ShouldReplace)
Go through the uses list for this definition and make each use point to "V" if the callback ShouldRep...
Definition Value.cpp:561
iterator_range< use_iterator > uses()
Definition Value.h:380
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:319
An efficient, type-erasing, non-owning reference to a callable.
const ParentTy * getParent() const
Definition ilist_node.h:34
self_iterator getIterator()
Definition ilist_node.h:123
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition ilist_node.h:348
A raw_ostream that writes to an SmallVector or SmallString.
StringRef str() const
Return a StringRef for the vector contents.
The virtual file system interface.
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(const Twine &Name, int64_t FileSize=-1, bool RequiresNullTerminator=true, bool IsVolatile=false, bool IsText=true)
This is a convenience method that opens a file, gets its content and then closes the file.
virtual llvm::ErrorOr< Status > status(const Twine &Path)=0
Get the status of the entry at Path, if one exists.
CallInst * Call
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
@ AMDGPU_KERNEL
Used for AMDGPU code object kernels.
@ SPIR_KERNEL
Used for SPIR kernel functions.
@ PTX_Kernel
Call to a PTX kernel. Passes all arguments in parameter space.
@ BasicBlock
Various leaf nodes.
Definition ISDOpcodes.h:81
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
Flag
These should be considered private to the implementation of the MCInstrDesc class.
constexpr StringLiteral MaxNTID("nvvm.maxntid")
constexpr StringLiteral MaxClusterRank("nvvm.maxclusterrank")
initializer< Ty > init(const Ty &Val)
@ User
could "use" a pointer
LLVM_ABI GlobalVariable * emitOffloadingEntry(Module &M, object::OffloadKind Kind, Constant *Addr, StringRef Name, uint64_t Size, uint32_t Flags, uint64_t Data, Constant *AuxAddr=nullptr)
Definition Utility.cpp: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:149
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition Format.h:102
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
AtomicOrdering
Atomic ordering for LLVM's memory model.
constexpr T divideCeil(U Numerator, V Denominator)
Returns the integer ceil(Numerator / Denominator).
Definition MathExtras.h: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),...