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
5272 for (auto En : enumerate(ReductionInfos)) {
5273 unsigned Index = En.index();
5274 const ReductionInfo &RI = En.value();
5275 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5276 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5277 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5278 }
5279
5280 // Emit a call to the runtime function that orchestrates the reduction.
5281 // Declare the reduction function in the process.
5282 Type *IndexTy = Builder.getIndexTy(
5283 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5284 Function *Func = Builder.GetInsertBlock()->getParent();
5285 Module *Module = Func->getParent();
5286 uint32_t SrcLocStrSize;
5287 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5288 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5289 return RI.AtomicReductionGen;
5290 });
5291 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5292 CanGenerateAtomic
5293 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5294 : IdentFlag(0));
5295 Value *ThreadId = getOrCreateThreadID(Ident);
5296 Constant *NumVariables = Builder.getInt32(NumReductions);
5297 const DataLayout &DL = Module->getDataLayout();
5298 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5299 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5300 Function *ReductionFunc = getFreshReductionFunc(*Module);
5301 Value *Lock = getOMPCriticalRegionLock(".reduction");
5303 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5304 : RuntimeFunction::OMPRTL___kmpc_reduce);
5305 CallInst *ReduceCall =
5306 createRuntimeFunctionCall(ReduceFunc,
5307 {Ident, ThreadId, NumVariables, RedArraySize,
5308 RedArray, ReductionFunc, Lock},
5309 "reduce");
5310
5311 // Create final reduction entry blocks for the atomic and non-atomic case.
5312 // Emit IR that dispatches control flow to one of the blocks based on the
5313 // reduction supporting the atomic mode.
5314 BasicBlock *NonAtomicRedBlock =
5315 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5316 BasicBlock *AtomicRedBlock =
5317 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5318 SwitchInst *Switch =
5319 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5320 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5321 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5322
5323 // Populate the non-atomic reduction using the elementwise reduction function.
5324 // This loads the elements from the global and private variables and reduces
5325 // them before storing back the result to the global variable.
5326 Builder.SetInsertPoint(NonAtomicRedBlock);
5327 for (auto En : enumerate(ReductionInfos)) {
5328 const ReductionInfo &RI = En.value();
5330 // We have one less load for by-ref case because that load is now inside of
5331 // the reduction region
5332 Value *RedValue = RI.Variable;
5333 if (!IsByRef[En.index()]) {
5334 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5335 "red.value." + Twine(En.index()));
5336 }
5337 Value *PrivateRedValue =
5338 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5339 "red.private.value." + Twine(En.index()));
5340 Value *Reduced;
5341 InsertPointOrErrorTy AfterIP =
5342 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5343 if (!AfterIP)
5344 return AfterIP.takeError();
5345 Builder.restoreIP(*AfterIP);
5346
5347 if (!Builder.GetInsertBlock())
5348 return InsertPointTy();
5349 // for by-ref case, the load is inside of the reduction region
5350 if (!IsByRef[En.index()])
5351 Builder.CreateStore(Reduced, RI.Variable);
5352 }
5353 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5354 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5355 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5356 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5357 Builder.CreateBr(ContinuationBlock);
5358
5359 // Populate the atomic reduction using the atomic elementwise reduction
5360 // function. There are no loads/stores here because they will be happening
5361 // inside the atomic elementwise reduction.
5362 Builder.SetInsertPoint(AtomicRedBlock);
5363 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5364 for (const ReductionInfo &RI : ReductionInfos) {
5366 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5367 if (!AfterIP)
5368 return AfterIP.takeError();
5369 Builder.restoreIP(*AfterIP);
5370 if (!Builder.GetInsertBlock())
5371 return InsertPointTy();
5372 }
5373 Builder.CreateBr(ContinuationBlock);
5374 } else {
5375 Builder.CreateUnreachable();
5376 }
5377
5378 // Populate the outlined reduction function using the elementwise reduction
5379 // function. Partial values are extracted from the type-erased array of
5380 // pointers to private variables.
5381 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5382 IsByRef, /*isGPU=*/false);
5383 if (Err)
5384 return Err;
5385
5386 if (!Builder.GetInsertBlock())
5387 return InsertPointTy();
5388
5389 Builder.SetInsertPoint(ContinuationBlock);
5390 return Builder.saveIP();
5391}
5392
5395 BodyGenCallbackTy BodyGenCB,
5396 FinalizeCallbackTy FiniCB) {
5397 if (!updateToLocation(Loc))
5398 return Loc.IP;
5399
5400 Directive OMPD = Directive::OMPD_master;
5401 uint32_t SrcLocStrSize;
5402 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5403 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5404 Value *ThreadId = getOrCreateThreadID(Ident);
5405 Value *Args[] = {Ident, ThreadId};
5406
5407 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5408 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5409
5410 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5411 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5412
5413 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5414 /*Conditional*/ true, /*hasFinalize*/ true);
5415}
5416
5419 BodyGenCallbackTy BodyGenCB,
5420 FinalizeCallbackTy FiniCB, Value *Filter) {
5422 if (!updateToLocation(Loc))
5423 return Loc.IP;
5424
5425 Directive OMPD = Directive::OMPD_masked;
5426 uint32_t SrcLocStrSize;
5427 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5428 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5429 Value *ThreadId = getOrCreateThreadID(Ident);
5430 Value *Args[] = {Ident, ThreadId, Filter};
5431 Value *ArgsEnd[] = {Ident, ThreadId};
5432
5433 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5434 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5435
5436 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5437 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5438
5439 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5440 /*Conditional*/ true, /*hasFinalize*/ true);
5441}
5442
5444 llvm::FunctionCallee Callee,
5446 const llvm::Twine &Name) {
5447 llvm::CallInst *Call = Builder.CreateCall(
5448 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5449 Call->setDoesNotThrow();
5450 return Call;
5451}
5452
5453// Expects input basic block is dominated by BeforeScanBB.
5454// Once Scan directive is encountered, the code after scan directive should be
5455// dominated by AfterScanBB. Scan directive splits the code sequence to
5456// scan and input phase. Based on whether inclusive or exclusive
5457// clause is used in the scan directive and whether input loop or scan loop
5458// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5459// input loop and second is the scan loop. The code generated handles only
5460// inclusive scans now.
5462 const LocationDescription &Loc, InsertPointTy AllocaIP,
5463 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5464 bool IsInclusive, ScanInfo *ScanRedInfo) {
5465 if (ScanRedInfo->OMPFirstScanLoop) {
5466 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5467 ScanVarsType, ScanRedInfo);
5468 if (Err)
5469 return Err;
5470 }
5471 if (!updateToLocation(Loc))
5472 return Loc.IP;
5473
5474 llvm::Value *IV = ScanRedInfo->IV;
5475
5476 if (ScanRedInfo->OMPFirstScanLoop) {
5477 // Emit buffer[i] = red; at the end of the input phase.
5478 for (size_t i = 0; i < ScanVars.size(); i++) {
5479 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5480 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5481 Type *DestTy = ScanVarsType[i];
5482 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5483 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5484
5485 Builder.CreateStore(Src, Val);
5486 }
5487 }
5488 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5489 emitBlock(ScanRedInfo->OMPScanDispatch,
5490 Builder.GetInsertBlock()->getParent());
5491
5492 if (!ScanRedInfo->OMPFirstScanLoop) {
5493 IV = ScanRedInfo->IV;
5494 // Emit red = buffer[i]; at the entrance to the scan phase.
5495 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5496 for (size_t i = 0; i < ScanVars.size(); i++) {
5497 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5498 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5499 Type *DestTy = ScanVarsType[i];
5500 Value *SrcPtr =
5501 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5502 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5503 Builder.CreateStore(Src, ScanVars[i]);
5504 }
5505 }
5506
5507 // TODO: Update it to CreateBr and remove dead blocks
5508 llvm::Value *CmpI = Builder.getInt1(true);
5509 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5510 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5511 ScanRedInfo->OMPAfterScanBlock);
5512 } else {
5513 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5514 ScanRedInfo->OMPBeforeScanBlock);
5515 }
5516 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5517 Builder.GetInsertBlock()->getParent());
5518 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5519 return Builder.saveIP();
5520}
5521
5522Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5523 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5524 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5525
5526 Builder.restoreIP(AllocaIP);
5527 // Create the shared pointer at alloca IP.
5528 for (size_t i = 0; i < ScanVars.size(); i++) {
5529 llvm::Value *BuffPtr =
5530 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5531 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5532 }
5533
5534 // Allocate temporary buffer by master thread
5535 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5536 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5537 Builder.restoreIP(CodeGenIP);
5538 Value *AllocSpan =
5539 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5540 for (size_t i = 0; i < ScanVars.size(); i++) {
5541 Type *IntPtrTy = Builder.getInt32Ty();
5542 Constant *Allocsize = ConstantExpr::getSizeOf(ScanVarsType[i]);
5543 Allocsize = ConstantExpr::getTruncOrBitCast(Allocsize, IntPtrTy);
5544 Value *Buff = Builder.CreateMalloc(IntPtrTy, ScanVarsType[i], Allocsize,
5545 AllocSpan, nullptr, "arr");
5546 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5547 }
5548 return Error::success();
5549 };
5550 // TODO: Perform finalization actions for variables. This has to be
5551 // called for variables which have destructors/finalizers.
5552 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5553
5554 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5555 llvm::Value *FilterVal = Builder.getInt32(0);
5557 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5558
5559 if (!AfterIP)
5560 return AfterIP.takeError();
5561 Builder.restoreIP(*AfterIP);
5562 BasicBlock *InputBB = Builder.GetInsertBlock();
5563 if (InputBB->hasTerminator())
5564 Builder.SetInsertPoint(InputBB->getTerminator());
5565 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5566 if (!AfterIP)
5567 return AfterIP.takeError();
5568 Builder.restoreIP(*AfterIP);
5569
5570 return Error::success();
5571}
5572
5573Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5574 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5575 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5576 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5577 Builder.restoreIP(CodeGenIP);
5578 for (ReductionInfo RedInfo : ReductionInfos) {
5579 Value *PrivateVar = RedInfo.PrivateVariable;
5580 Value *OrigVar = RedInfo.Variable;
5581 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5582 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5583
5584 Type *SrcTy = RedInfo.ElementType;
5585 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5586 "arrayOffset");
5587 Value *Src = Builder.CreateLoad(SrcTy, Val);
5588
5589 Builder.CreateStore(Src, OrigVar);
5590 Builder.CreateFree(Buff);
5591 }
5592 return Error::success();
5593 };
5594 // TODO: Perform finalization actions for variables. This has to be
5595 // called for variables which have destructors/finalizers.
5596 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5597
5598 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5599 Builder.SetInsertPoint(TI);
5600 else
5601 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5602
5603 llvm::Value *FilterVal = Builder.getInt32(0);
5605 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5606
5607 if (!AfterIP)
5608 return AfterIP.takeError();
5609 Builder.restoreIP(*AfterIP);
5610 BasicBlock *InputBB = Builder.GetInsertBlock();
5611 if (InputBB->hasTerminator())
5612 Builder.SetInsertPoint(InputBB->getTerminator());
5613 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5614 if (!AfterIP)
5615 return AfterIP.takeError();
5616 Builder.restoreIP(*AfterIP);
5617 return Error::success();
5618}
5619
5621 const LocationDescription &Loc,
5623 ScanInfo *ScanRedInfo) {
5624
5625 if (!updateToLocation(Loc))
5626 return Loc.IP;
5627 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5628 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5629 Builder.restoreIP(CodeGenIP);
5630 Function *CurFn = Builder.GetInsertBlock()->getParent();
5631 // for (int k = 0; k <= ceil(log2(n)); ++k)
5632 llvm::BasicBlock *LoopBB =
5633 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5634 llvm::BasicBlock *ExitBB =
5635 splitBB(Builder, false, "omp.outer.log.scan.exit");
5637 Builder.GetInsertBlock()->getModule(),
5638 (llvm::Intrinsic::ID)llvm::Intrinsic::log2, Builder.getDoubleTy());
5639 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5640 llvm::Value *Arg =
5641 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5642 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5644 Builder.GetInsertBlock()->getModule(),
5645 (llvm::Intrinsic::ID)llvm::Intrinsic::ceil, Builder.getDoubleTy());
5646 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5647 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5648 llvm::Value *NMin1 = Builder.CreateNUWSub(
5649 ScanRedInfo->Span,
5650 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5651 Builder.SetInsertPoint(InputBB);
5652 Builder.CreateBr(LoopBB);
5653 emitBlock(LoopBB, CurFn);
5654 Builder.SetInsertPoint(LoopBB);
5655
5656 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5657 // size pow2k = 1;
5658 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5659 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5660 InputBB);
5661 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5662 InputBB);
5663 // for (size i = n - 1; i >= 2 ^ k; --i)
5664 // tmp[i] op= tmp[i-pow2k];
5665 llvm::BasicBlock *InnerLoopBB =
5666 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5667 llvm::BasicBlock *InnerExitBB =
5668 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5669 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5670 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5671 emitBlock(InnerLoopBB, CurFn);
5672 Builder.SetInsertPoint(InnerLoopBB);
5673 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5674 IVal->addIncoming(NMin1, LoopBB);
5675 for (ReductionInfo RedInfo : ReductionInfos) {
5676 Value *ReductionVal = RedInfo.PrivateVariable;
5677 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5678 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5679 Type *DestTy = RedInfo.ElementType;
5680 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5681 Value *LHSPtr =
5682 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5683 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5684 Value *RHSPtr =
5685 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5686 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5687 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5688 llvm::Value *Result;
5689 InsertPointOrErrorTy AfterIP =
5690 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5691 if (!AfterIP)
5692 return AfterIP.takeError();
5693 Builder.CreateStore(Result, LHSPtr);
5694 }
5695 llvm::Value *NextIVal = Builder.CreateNUWSub(
5696 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5697 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5698 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5699 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5700 emitBlock(InnerExitBB, CurFn);
5701 llvm::Value *Next = Builder.CreateNUWAdd(
5702 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5703 Counter->addIncoming(Next, Builder.GetInsertBlock());
5704 // pow2k <<= 1;
5705 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5706 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5707 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5708 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5709 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5710 return Error::success();
5711 };
5712
5713 // TODO: Perform finalization actions for variables. This has to be
5714 // called for variables which have destructors/finalizers.
5715 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5716
5717 llvm::Value *FilterVal = Builder.getInt32(0);
5719 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5720
5721 if (!AfterIP)
5722 return AfterIP.takeError();
5723 Builder.restoreIP(*AfterIP);
5724 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5725
5726 if (!AfterIP)
5727 return AfterIP.takeError();
5728 Builder.restoreIP(*AfterIP);
5729 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5730 if (Err)
5731 return Err;
5732
5733 return AfterIP;
5734}
5735
5736Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5737 llvm::function_ref<Error()> InputLoopGen,
5738 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5739 ScanInfo *ScanRedInfo) {
5740
5741 {
5742 // Emit loop with input phase:
5743 // for (i: 0..<num_iters>) {
5744 // <input phase>;
5745 // buffer[i] = red;
5746 // }
5747 ScanRedInfo->OMPFirstScanLoop = true;
5748 Error Err = InputLoopGen();
5749 if (Err)
5750 return Err;
5751 }
5752 {
5753 // Emit loop with scan phase:
5754 // for (i: 0..<num_iters>) {
5755 // red = buffer[i];
5756 // <scan phase>;
5757 // }
5758 ScanRedInfo->OMPFirstScanLoop = false;
5759 Error Err = ScanLoopGen(Builder);
5760 if (Err)
5761 return Err;
5762 }
5763 return Error::success();
5764}
5765
5766void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5767 Function *Fun = Builder.GetInsertBlock()->getParent();
5768 ScanRedInfo->OMPScanDispatch =
5769 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5770 ScanRedInfo->OMPAfterScanBlock =
5771 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5772 ScanRedInfo->OMPBeforeScanBlock =
5773 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5774 ScanRedInfo->OMPScanLoopExit =
5775 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5776}
5778 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5779 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5780 Module *M = F->getParent();
5781 LLVMContext &Ctx = M->getContext();
5782 Type *IndVarTy = TripCount->getType();
5783
5784 // Create the basic block structure.
5785 BasicBlock *Preheader =
5786 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5787 BasicBlock *Header =
5788 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5789 BasicBlock *Cond =
5790 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5791 BasicBlock *Body =
5792 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5793 BasicBlock *Latch =
5794 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5795 BasicBlock *Exit =
5796 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5797 BasicBlock *After =
5798 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5799
5800 // Use specified DebugLoc for new instructions.
5801 Builder.SetCurrentDebugLocation(DL);
5802
5803 Builder.SetInsertPoint(Preheader);
5804 Builder.CreateBr(Header);
5805
5806 Builder.SetInsertPoint(Header);
5807 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5808 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5809 Builder.CreateBr(Cond);
5810
5811 Builder.SetInsertPoint(Cond);
5812 Value *Cmp =
5813 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5814 Builder.CreateCondBr(Cmp, Body, Exit);
5815
5816 Builder.SetInsertPoint(Body);
5817 Builder.CreateBr(Latch);
5818
5819 Builder.SetInsertPoint(Latch);
5820 // Decide whether the induction variable increment can carry nsw.
5821 //
5822 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5823 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5824 // for valid programs 0 <= count <= INT_MAX always holds.
5825 //
5826 // Collapsed loops: the trip count is a product that can overflow i32 even for
5827 // a conforming program, so nsw is kept only when the product is a constant
5828 // that provably fits, dropped otherwise.
5829 bool HasNSW = Config.hasNoSignedWrap();
5830 if (HasNSW) {
5831 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5832 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5834 if (CI->getValue().ugt(SignedMax))
5835 HasNSW = false;
5836 } else if (IsCollapsed) {
5837 HasNSW = false;
5838 }
5839 }
5840 Value *Next =
5841 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5842 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5843 Builder.CreateBr(Header);
5844 IndVarPHI->addIncoming(Next, Latch);
5845
5846 Builder.SetInsertPoint(Exit);
5847 Builder.CreateBr(After);
5848
5849 // Remember and return the canonical control flow.
5850 LoopInfos.emplace_front();
5851 CanonicalLoopInfo *CL = &LoopInfos.front();
5852
5853 CL->Header = Header;
5854 CL->Cond = Cond;
5855 CL->Latch = Latch;
5856 CL->Exit = Exit;
5857
5858#ifndef NDEBUG
5859 CL->assertOK();
5860#endif
5861 return CL;
5862}
5863
5866 LoopBodyGenCallbackTy BodyGenCB,
5867 Value *TripCount, const Twine &Name) {
5868 BasicBlock *BB = Loc.IP.getBlock();
5869 BasicBlock *NextBB = BB->getNextNode();
5870
5871 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5872 NextBB, NextBB, Name);
5873 BasicBlock *After = CL->getAfter();
5874
5875 // If location is not set, don't connect the loop.
5876 if (updateToLocation(Loc)) {
5877 // Split the loop at the insertion point: Branch to the preheader and move
5878 // every following instruction to after the loop (the After BB). Also, the
5879 // new successor is the loop's after block.
5880 spliceBB(Builder, After, /*CreateBranch=*/false);
5881 Builder.CreateBr(CL->getPreheader());
5882 }
5883
5884 // Emit the body content. We do it after connecting the loop to the CFG to
5885 // avoid that the callback encounters degenerate BBs.
5886 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5887 return Err;
5888
5889#ifndef NDEBUG
5890 CL->assertOK();
5891#endif
5892 return CL;
5893}
5894
5896 ScanInfos.emplace_front();
5897 ScanInfo *Result = &ScanInfos.front();
5898 return Result;
5899}
5900
5904 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5905 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5906 LocationDescription ComputeLoc =
5907 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5908 updateToLocation(ComputeLoc);
5909
5911
5913 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5914 ScanRedInfo->Span = TripCount;
5915 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5916 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5917
5918 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5919 Builder.restoreIP(CodeGenIP);
5920 ScanRedInfo->IV = IV;
5921 createScanBBs(ScanRedInfo);
5922 BasicBlock *InputBlock = Builder.GetInsertBlock();
5923 Instruction *Terminator = InputBlock->getTerminator();
5924 assert(Terminator->getNumSuccessors() == 1);
5925 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5926 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5927 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5928 Builder.GetInsertBlock()->getParent());
5929 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5930 emitBlock(ScanRedInfo->OMPScanLoopExit,
5931 Builder.GetInsertBlock()->getParent());
5932 Builder.CreateBr(ContinueBlock);
5933 Builder.SetInsertPoint(
5934 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5935 return BodyGenCB(Builder.saveIP(), IV);
5936 };
5937
5938 const auto &&InputLoopGen = [&]() -> Error {
5940 createCanonicalLoop(Builder, BodyGen, Start, Stop, Step, IsSigned,
5941 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5942 if (!LoopInfo)
5943 return LoopInfo.takeError();
5944 Result.push_back(*LoopInfo);
5945 Builder.restoreIP((*LoopInfo)->getAfterIP());
5946 return Error::success();
5947 };
5948 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5950 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5951 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5952 if (!LoopInfo)
5953 return LoopInfo.takeError();
5954 Result.push_back(*LoopInfo);
5955 Builder.restoreIP((*LoopInfo)->getAfterIP());
5956 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5957 return Error::success();
5958 };
5959 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5960 if (Err)
5961 return Err;
5962 return Result;
5963}
5964
5966 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5967 bool IsSigned, bool InclusiveStop, const Twine &Name) {
5968
5969 // Consider the following difficulties (assuming 8-bit signed integers):
5970 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
5971 // DO I = 1, 100, 50
5972 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
5973 // DO I = 100, 0, -128
5974
5975 // Start, Stop and Step must be of the same integer type.
5976 auto *IndVarTy = cast<IntegerType>(Start->getType());
5977 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
5978 assert(IndVarTy == Step->getType() && "Step type mismatch");
5979
5981
5982 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
5983 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
5984
5985 // Like Step, but always positive.
5986 Value *Incr = Step;
5987
5988 // Distance between Start and Stop; always positive.
5989 Value *Span;
5990
5991 // Condition whether there are no iterations are executed at all, e.g. because
5992 // UB < LB.
5993 Value *ZeroCmp;
5994
5995 if (IsSigned) {
5996 // Ensure that increment is positive. If not, negate and invert LB and UB.
5997 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
5998 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
5999 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6000 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6001 Span = Builder.CreateSub(UB, LB, "", false, true);
6002 ZeroCmp = Builder.CreateICmp(
6003 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6004 } else {
6005 Span = Builder.CreateSub(Stop, Start, "", true);
6006 ZeroCmp = Builder.CreateICmp(
6007 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6008 }
6009
6010 Value *CountIfLooping;
6011 if (InclusiveStop) {
6012 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6013 } else {
6014 // Avoid incrementing past stop since it could overflow.
6015 Value *CountIfTwo = Builder.CreateAdd(
6016 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6017 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6018 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6019 }
6020
6021 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6022 "omp_" + Name + ".tripcount");
6023}
6024
6027 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6028 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6029 ScanInfo *ScanRedInfo) {
6030 LocationDescription ComputeLoc =
6031 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6032
6034 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6035
6036 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6037 Builder.restoreIP(CodeGenIP);
6038 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6039 /*HasNSW=*/Config.hasNoSignedWrap());
6040 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6041 /*HasNSW=*/Config.hasNoSignedWrap());
6042 if (InScan)
6043 ScanRedInfo->IV = IndVar;
6044 return BodyGenCB(Builder.saveIP(), IndVar);
6045 };
6046 LocationDescription LoopLoc =
6047 ComputeIP.isSet()
6048 ? Loc
6049 : LocationDescription(Builder.saveIP(),
6050 Builder.getCurrentDebugLocation());
6051 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6052}
6053
6054// Returns an LLVM function to call for initializing loop bounds using OpenMP
6055// static scheduling for composite `distribute parallel for` depending on
6056// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6057// integers as unsigned similarly to CanonicalLoopInfo.
6058static FunctionCallee
6060 OpenMPIRBuilder &OMPBuilder) {
6061 unsigned Bitwidth = Ty->getIntegerBitWidth();
6062 if (Bitwidth == 32)
6063 return OMPBuilder.getOrCreateRuntimeFunction(
6064 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6065 if (Bitwidth == 64)
6066 return OMPBuilder.getOrCreateRuntimeFunction(
6067 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6068 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6069}
6070
6071// Returns an LLVM function to call for initializing loop bounds using OpenMP
6072// static scheduling depending on `type`. Only i32 and i64 are supported by the
6073// runtime. Always interpret integers as unsigned similarly to
6074// CanonicalLoopInfo.
6076 OpenMPIRBuilder &OMPBuilder) {
6077 unsigned Bitwidth = Ty->getIntegerBitWidth();
6078 if (Bitwidth == 32)
6079 return OMPBuilder.getOrCreateRuntimeFunction(
6080 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6081 if (Bitwidth == 64)
6082 return OMPBuilder.getOrCreateRuntimeFunction(
6083 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6084 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6085}
6086
6087OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6088 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6089 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6090 OMPScheduleType DistScheduleSchedType) {
6091 assert(CLI->isValid() && "Requires a valid canonical loop");
6092 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6093 "Require dedicated allocate IP");
6094
6095 // Set up the source location value for OpenMP runtime.
6096 Builder.restoreIP(CLI->getPreheaderIP());
6097 Builder.SetCurrentDebugLocation(DL);
6098
6099 uint32_t SrcLocStrSize;
6100 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6102 switch (LoopType) {
6103 case WorksharingLoopType::ForStaticLoop:
6104 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6105 break;
6106 case WorksharingLoopType::DistributeStaticLoop:
6107 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6108 break;
6109 case WorksharingLoopType::DistributeForStaticLoop:
6110 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6111 break;
6112 }
6113 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6114
6115 // Declare useful OpenMP runtime functions.
6116 Value *IV = CLI->getIndVar();
6117 Type *IVTy = IV->getType();
6118 FunctionCallee StaticInit =
6119 LoopType == WorksharingLoopType::DistributeForStaticLoop
6120 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6121 : getKmpcForStaticInitForType(IVTy, M, *this);
6122 FunctionCallee StaticFini =
6123 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6124
6125 // Allocate space for computed loop bounds as expected by the "init" function.
6126 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6127
6128 Type *I32Type = Type::getInt32Ty(M.getContext());
6129 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6130 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6131 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6132 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6133 CLI->setLastIter(PLastIter);
6134
6135 // At the end of the preheader, prepare for calling the "init" function by
6136 // storing the current loop bounds into the allocated space. A canonical loop
6137 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6138 // and produces an inclusive upper bound.
6139 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6140 Constant *Zero = ConstantInt::get(IVTy, 0);
6141 Constant *One = ConstantInt::get(IVTy, 1);
6142 Builder.CreateStore(Zero, PLowerBound);
6143 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6144 Builder.CreateStore(UpperBound, PUpperBound);
6145 Builder.CreateStore(One, PStride);
6146
6147 Value *ThreadNum =
6148 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6149
6150 OMPScheduleType SchedType =
6151 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6152 ? OMPScheduleType::OrderedDistribute
6154 Constant *SchedulingType =
6155 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6156
6157 // Call the "init" function and update the trip count of the loop with the
6158 // value it produced.
6159 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6160 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6161 this](Value *SchedulingType, auto &Builder) {
6162 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6163 PLowerBound, PUpperBound});
6164 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6165 Value *PDistUpperBound =
6166 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6167 Args.push_back(PDistUpperBound);
6168 }
6169 Args.append({PStride, One, Zero});
6170 createRuntimeFunctionCall(StaticInit, Args);
6171 };
6172 BuildInitCall(SchedulingType, Builder);
6173 if (HasDistSchedule &&
6174 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6175 Constant *DistScheduleSchedType = ConstantInt::get(
6176 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6177 // We want to emit a second init function call for the dist_schedule clause
6178 // to the Distribute construct. This should only be done however if a
6179 // Workshare Loop is nested within a Distribute Construct
6180 BuildInitCall(DistScheduleSchedType, Builder);
6181 }
6182 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6183 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6184 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6185 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6186 CLI->setTripCount(TripCount);
6187
6188 // Update all uses of the induction variable except the one in the condition
6189 // block that compares it with the actual upper bound, and the increment in
6190 // the latch block.
6191
6192 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6193 Builder.SetInsertPoint(CLI->getBody(),
6194 CLI->getBody()->getFirstInsertionPt());
6195 Builder.SetCurrentDebugLocation(DL);
6196 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6197 /*HasNSW=*/Config.hasNoSignedWrap());
6198 });
6199
6200 // In the "exit" block, call the "fini" function.
6201 Builder.SetInsertPoint(CLI->getExit(),
6202 CLI->getExit()->getTerminator()->getIterator());
6203 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6204
6205 // Add the barrier if requested.
6206 if (NeedsBarrier) {
6207 InsertPointOrErrorTy BarrierIP =
6209 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6210 /* CheckCancelFlag */ false);
6211 if (!BarrierIP)
6212 return BarrierIP.takeError();
6213 }
6214
6215 InsertPointTy AfterIP = CLI->getAfterIP();
6216 CLI->invalidate();
6217
6218 return AfterIP;
6219}
6220
6221static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6222 LoopInfo &LI);
6223static void addLoopMetadata(CanonicalLoopInfo *Loop,
6225
6227 LLVMContext &Ctx, Loop *Loop,
6229 SmallVector<Metadata *> &LoopMDList) {
6230 SmallSet<BasicBlock *, 8> Reachable;
6231
6232 // Get the basic blocks from the loop in which memref instructions
6233 // can be found.
6234 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6235 // preferably without running any passes.
6236 for (BasicBlock *Block : Loop->getBlocks()) {
6237 if (Block == CLI->getCond() || Block == CLI->getHeader())
6238 continue;
6239 Reachable.insert(Block);
6240 }
6241
6242 // Add access group metadata to memory-access instructions.
6244 for (BasicBlock *BB : Reachable)
6246 // TODO: If the loop has existing parallel access metadata, have
6247 // to combine two lists.
6248 LoopMDList.push_back(MDNode::get(
6249 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6250}
6251
6253OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6254 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6255 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6256 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6257 assert(CLI->isValid() && "Requires a valid canonical loop");
6258 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6259
6260 LLVMContext &Ctx = CLI->getFunction()->getContext();
6261 Value *IV = CLI->getIndVar();
6262 Value *OrigTripCount = CLI->getTripCount();
6263 Type *IVTy = IV->getType();
6264 assert(IVTy->getIntegerBitWidth() <= 64 &&
6265 "Max supported tripcount bitwidth is 64 bits");
6266 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6267 : Type::getInt64Ty(Ctx);
6268 Type *I32Type = Type::getInt32Ty(M.getContext());
6269 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6270 Constant *One = ConstantInt::get(InternalIVTy, 1);
6271
6272 Function *F = CLI->getFunction();
6273 // Blocks must have terminators.
6274 // FIXME: Don't run analyses on incomplete/invalid IR.
6275 SmallVector<Instruction *> UIs;
6276 for (BasicBlock &BB : *F)
6277 if (!BB.hasTerminator())
6278 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6280 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6281 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6282 LoopAnalysis LIA;
6283 LoopInfo &&LI = LIA.run(*F, FAM);
6284 for (Instruction *I : UIs)
6285 I->eraseFromParent();
6286 Loop *L = LI.getLoopFor(CLI->getHeader());
6287 SmallVector<Metadata *> LoopMDList;
6288 if (ChunkSize || DistScheduleChunkSize)
6289 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6290 addLoopMetadata(CLI, LoopMDList);
6291
6292 // Declare useful OpenMP runtime functions.
6293 FunctionCallee StaticInit =
6294 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6295 FunctionCallee StaticFini =
6296 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6297
6298 // Allocate space for computed loop bounds as expected by the "init" function.
6299 Builder.restoreIP(AllocaIP);
6300 Builder.SetCurrentDebugLocation(DL);
6301 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6302 Value *PLowerBound =
6303 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6304 Value *PUpperBound =
6305 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6306 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6307 CLI->setLastIter(PLastIter);
6308
6309 // Set up the source location value for the OpenMP runtime.
6310 Builder.restoreIP(CLI->getPreheaderIP());
6311 Builder.SetCurrentDebugLocation(DL);
6312
6313 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6314 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6315 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6316 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6317 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6318 "distschedulechunksize");
6319 Value *CastedTripCount =
6320 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6321
6322 Constant *SchedulingType =
6323 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6324 Constant *DistSchedulingType =
6325 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6326 Builder.CreateStore(Zero, PLowerBound);
6327 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6328 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6329 Value *UpperBound =
6330 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6331 Builder.CreateStore(UpperBound, PUpperBound);
6332 Builder.CreateStore(One, PStride);
6333
6334 // Call the "init" function and update the trip count of the loop with the
6335 // value it produced.
6336 uint32_t SrcLocStrSize;
6337 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6338 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6339 if (DistScheduleSchedType != OMPScheduleType::None) {
6340 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6341 }
6342 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6343 Value *ThreadNum =
6344 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6345 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6346 PUpperBound, PStride, One,
6347 this](Value *SchedulingType, Value *ChunkSize,
6348 auto &Builder) {
6350 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6351 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6352 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6353 /*pstride=*/PStride, /*incr=*/One,
6354 /*chunk=*/ChunkSize});
6355 };
6356 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6357 if (DistScheduleSchedType != OMPScheduleType::None &&
6358 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6359 SchedType != OMPScheduleType::OrderedDistribute) {
6360 // We want to emit a second init function call for the dist_schedule clause
6361 // to the Distribute construct. This should only be done however if a
6362 // Workshare Loop is nested within a Distribute Construct
6363 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6364 }
6365
6366 // Load values written by the "init" function.
6367 Value *FirstChunkStart =
6368 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6369 Value *FirstChunkStop =
6370 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6371 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6372 Value *ChunkRange =
6373 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6374 Value *NextChunkStride =
6375 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6376
6377 // Create outer "dispatch" loop for enumerating the chunks.
6378 BasicBlock *DispatchEnter = splitBB(Builder, true);
6379 Value *DispatchCounter;
6380
6381 // It is safe to assume this didn't return an error because the callback
6382 // passed into createCanonicalLoop is the only possible error source, and it
6383 // always returns success.
6384 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6385 {Builder.saveIP(), DL},
6386 [&](InsertPointTy BodyIP, Value *Counter) {
6387 DispatchCounter = Counter;
6388 return Error::success();
6389 },
6390 FirstChunkStart, CastedTripCount, NextChunkStride,
6391 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6392 "dispatch"));
6393
6394 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6395 // not have to preserve the canonical invariant.
6396 BasicBlock *DispatchBody = DispatchCLI->getBody();
6397 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6398 BasicBlock *DispatchExit = DispatchCLI->getExit();
6399 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6400 DispatchCLI->invalidate();
6401
6402 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6403 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6404 redirectTo(CLI->getExit(), DispatchLatch, DL);
6405 redirectTo(DispatchBody, DispatchEnter, DL);
6406
6407 // Prepare the prolog of the chunk loop.
6408 Builder.restoreIP(CLI->getPreheaderIP());
6409 Builder.SetCurrentDebugLocation(DL);
6410
6411 // Compute the number of iterations of the chunk loop.
6412 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6413 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6414 Value *IsLastChunk =
6415 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6416 Value *CountUntilOrigTripCount =
6417 Builder.CreateSub(CastedTripCount, DispatchCounter);
6418 Value *ChunkTripCount = Builder.CreateSelect(
6419 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6420 Value *BackcastedChunkTC =
6421 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6422 CLI->setTripCount(BackcastedChunkTC);
6423
6424 // Update all uses of the induction variable except the one in the condition
6425 // block that compares it with the actual upper bound, and the increment in
6426 // the latch block.
6427 Value *BackcastedDispatchCounter =
6428 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6429 CLI->mapIndVar([&](Instruction *) -> Value * {
6430 Builder.restoreIP(CLI->getBodyIP());
6431 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6432 });
6433
6434 // In the "exit" block, call the "fini" function.
6435 Builder.SetInsertPoint(DispatchExit, DispatchExit->getFirstInsertionPt());
6436 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6437
6438 // Add the barrier if requested.
6439 if (NeedsBarrier) {
6440 InsertPointOrErrorTy AfterIP =
6441 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6442 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6443 if (!AfterIP)
6444 return AfterIP.takeError();
6445 }
6446
6447#ifndef NDEBUG
6448 // Even though we currently do not support applying additional methods to it,
6449 // the chunk loop should remain a canonical loop.
6450 CLI->assertOK();
6451#endif
6452
6453 return InsertPointTy(DispatchAfter, DispatchAfter->getFirstInsertionPt());
6454}
6455
6456// Returns an LLVM function to call for executing an OpenMP static worksharing
6457// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6458// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6459static FunctionCallee
6461 WorksharingLoopType LoopType) {
6462 unsigned Bitwidth = Ty->getIntegerBitWidth();
6463 Module &M = OMPBuilder->M;
6464 switch (LoopType) {
6465 case WorksharingLoopType::ForStaticLoop:
6466 if (Bitwidth == 32)
6467 return OMPBuilder->getOrCreateRuntimeFunction(
6468 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6469 if (Bitwidth == 64)
6470 return OMPBuilder->getOrCreateRuntimeFunction(
6471 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6472 break;
6473 case WorksharingLoopType::DistributeStaticLoop:
6474 if (Bitwidth == 32)
6475 return OMPBuilder->getOrCreateRuntimeFunction(
6476 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6477 if (Bitwidth == 64)
6478 return OMPBuilder->getOrCreateRuntimeFunction(
6479 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6480 break;
6481 case WorksharingLoopType::DistributeForStaticLoop:
6482 if (Bitwidth == 32)
6483 return OMPBuilder->getOrCreateRuntimeFunction(
6484 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6485 if (Bitwidth == 64)
6486 return OMPBuilder->getOrCreateRuntimeFunction(
6487 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6488 break;
6489 }
6490 if (Bitwidth != 32 && Bitwidth != 64) {
6491 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6492 }
6493 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6494}
6495
6496// Inserts a call to proper OpenMP Device RTL function which handles
6497// loop worksharing.
6499 WorksharingLoopType LoopType,
6500 BasicBlock *InsertBlock, Value *Ident,
6501 Value *LoopBodyArg, Value *TripCount,
6502 Function &LoopBodyFn, bool NoLoop) {
6503 Type *TripCountTy = TripCount->getType();
6504 Module &M = OMPBuilder->M;
6505 IRBuilder<> &Builder = OMPBuilder->Builder;
6506 FunctionCallee RTLFn =
6507 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6508 SmallVector<Value *, 8> RealArgs;
6509 RealArgs.push_back(Ident);
6510 RealArgs.push_back(&LoopBodyFn);
6511 RealArgs.push_back(LoopBodyArg);
6512 RealArgs.push_back(TripCount);
6513 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6514 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6515 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6516 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6517 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6518 return;
6519 }
6520 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6521 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6522 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6523 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6524
6525 RealArgs.push_back(
6526 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6527 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6528 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6529 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6530 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6531 } else {
6532 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6533 }
6534
6535 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6536}
6537
6539 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6540 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6541 WorksharingLoopType LoopType, bool NoLoop) {
6542 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6543 BasicBlock *Preheader = CLI->getPreheader();
6544 Value *TripCount = CLI->getTripCount();
6545
6546 // After loop body outling, the loop body contains only set up
6547 // of loop body argument structure and the call to the outlined
6548 // loop body function. Firstly, we need to move setup of loop body args
6549 // into loop preheader.
6550 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6551 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6552
6553 // The next step is to remove the whole loop. We do not it need anymore.
6554 // That's why make an unconditional branch from loop preheader to loop
6555 // exit block
6556 Builder.restoreIP({Preheader, Preheader->end()});
6557 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6558 Preheader->getTerminator()->eraseFromParent();
6559 Builder.CreateBr(CLI->getExit());
6560
6561 // Delete dead loop blocks
6562 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6563 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6564 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6565 CleanUpInfo.EntryBB = CLI->getHeader();
6566 CleanUpInfo.ExitBB = CLI->getExit();
6567 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6568 DeleteDeadBlocks(BlocksToBeRemoved);
6569
6570 // Find the instruction which corresponds to loop body argument structure
6571 // and remove the call to loop body function instruction.
6572 Value *LoopBodyArg;
6573 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6574 assert(OutlinedFnUser &&
6575 "Expected unique undroppable user of outlined function");
6576 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6577 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6578 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6579 "Expected outlined function call to be located in loop preheader");
6580 // Check in case no argument structure has been passed.
6581 if (OutlinedFnCallInstruction->arg_size() > 1)
6582 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6583 else
6584 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6585 OutlinedFnCallInstruction->eraseFromParent();
6586
6587 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6588 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6589
6590 for (auto &ToBeDeletedItem : ToBeDeleted)
6591 ToBeDeletedItem->eraseFromParent();
6592 CLI->invalidate();
6593}
6594
6595OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6596 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6597 WorksharingLoopType LoopType, bool NoLoop) {
6598 uint32_t SrcLocStrSize;
6599 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6601 switch (LoopType) {
6602 case WorksharingLoopType::ForStaticLoop:
6603 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6604 break;
6605 case WorksharingLoopType::DistributeStaticLoop:
6606 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6607 break;
6608 case WorksharingLoopType::DistributeForStaticLoop:
6609 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6610 break;
6611 }
6612 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6613
6614 auto OI = std::make_unique<OutlineInfo>();
6615 OI->OuterAllocBB = CLI->getPreheader();
6616 Function *OuterFn = CLI->getPreheader()->getParent();
6617
6618 // Instructions which need to be deleted at the end of code generation
6619 SmallVector<Instruction *, 4> ToBeDeleted;
6620
6621 OI->OuterAllocBB = AllocaIP.getBlock();
6622
6623 // Mark the body loop as region which needs to be extracted
6624 OI->EntryBB = CLI->getBody();
6625 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6626 "omp.prelatch");
6627
6628 // Prepare loop body for extraction
6629 Builder.restoreIP({CLI->getPreheader(), CLI->getPreheader()->begin()});
6630
6631 // Insert new loop counter variable which will be used only in loop
6632 // body.
6633 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6634 Instruction *NewLoopCntLoad =
6635 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6636 // New loop counter instructions are redundant in the loop preheader when
6637 // code generation for workshare loop is finshed. That's why mark them as
6638 // ready for deletion.
6639 ToBeDeleted.push_back(NewLoopCntLoad);
6640 ToBeDeleted.push_back(NewLoopCnt);
6641
6642 // Analyse loop body region. Find all input variables which are used inside
6643 // loop body region.
6644 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6646 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6647
6648 CodeExtractorAnalysisCache CEAC(*OuterFn);
6649 CodeExtractor Extractor(Blocks,
6650 /* DominatorTree */ nullptr,
6651 /* AggregateArgs */ true,
6652 /* BlockFrequencyInfo */ nullptr,
6653 /* BranchProbabilityInfo */ nullptr,
6654 /* AssumptionCache */ nullptr,
6655 /* AllowVarArgs */ true,
6656 /* AllowAlloca */ true,
6657 /* AllocationBlock */ CLI->getPreheader(),
6658 /* DeallocationBlocks */ {},
6659 /* Suffix */ ".omp_wsloop",
6660 /* AggrArgsIn0AddrSpace */ true);
6661
6662 BasicBlock *CommonExit = nullptr;
6663 SetVector<Value *> SinkingCands, HoistingCands;
6664
6665 // Find allocas outside the loop body region which are used inside loop
6666 // body
6667 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6668
6669 // We need to model loop body region as the function f(cnt, loop_arg).
6670 // That's why we replace loop induction variable by the new counter
6671 // which will be one of loop body function argument
6673 CLI->getIndVar()->user_end());
6674 for (auto Use : Users) {
6675 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6676 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6677 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6678 }
6679 }
6680 }
6681 // Make sure that loop counter variable is not merged into loop body
6682 // function argument structure and it is passed as separate variable
6683 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6684
6685 // PostOutline CB is invoked when loop body function is outlined and
6686 // loop body is replaced by call to outlined function. We need to add
6687 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6688 // function will handle loop control logic.
6689 //
6690 OI->PostOutlineCB = [=, ToBeDeletedVec =
6691 std::move(ToBeDeleted)](Function &OutlinedFn) {
6692 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6693 LoopType, NoLoop);
6694 };
6695 addOutlineInfo(std::move(OI));
6696 return CLI->getAfterIP();
6697}
6698
6701 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6702 bool HasSimdModifier, bool HasMonotonicModifier,
6703 bool HasNonmonotonicModifier, bool HasOrderedClause,
6704 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6705 Value *DistScheduleChunkSize) {
6706 if (Config.isTargetDevice())
6707 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NoLoop);
6708 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6709 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6710 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6711
6712 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6713 OMPScheduleType::ModifierOrdered;
6714 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6715 if (HasDistSchedule) {
6716 DistScheduleSchedType = DistScheduleChunkSize
6717 ? OMPScheduleType::OrderedDistributeChunked
6718 : OMPScheduleType::OrderedDistribute;
6719 }
6720 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6721 case OMPScheduleType::BaseStatic:
6722 case OMPScheduleType::BaseDistribute:
6723 assert((!ChunkSize || !DistScheduleChunkSize) &&
6724 "No chunk size with static-chunked schedule");
6725 if (IsOrdered && !HasDistSchedule)
6726 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6727 NeedsBarrier, ChunkSize);
6728 // FIXME: Monotonicity ignored?
6729 if (DistScheduleChunkSize)
6730 return applyStaticChunkedWorkshareLoop(
6731 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6732 DistScheduleChunkSize, DistScheduleSchedType);
6733 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6734 HasDistSchedule);
6735
6736 case OMPScheduleType::BaseStaticChunked:
6737 case OMPScheduleType::BaseDistributeChunked:
6738 if (IsOrdered && !HasDistSchedule)
6739 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6740 NeedsBarrier, ChunkSize);
6741 // FIXME: Monotonicity ignored?
6742 return applyStaticChunkedWorkshareLoop(
6743 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6744 DistScheduleChunkSize, DistScheduleSchedType);
6745
6746 case OMPScheduleType::BaseRuntime:
6747 case OMPScheduleType::BaseAuto:
6748 case OMPScheduleType::BaseGreedy:
6749 case OMPScheduleType::BaseBalanced:
6750 case OMPScheduleType::BaseSteal:
6751 case OMPScheduleType::BaseRuntimeSimd:
6752 assert(!ChunkSize &&
6753 "schedule type does not support user-defined chunk sizes");
6754 [[fallthrough]];
6755 case OMPScheduleType::BaseGuidedSimd:
6756 case OMPScheduleType::BaseDynamicChunked:
6757 case OMPScheduleType::BaseGuidedChunked:
6758 case OMPScheduleType::BaseGuidedIterativeChunked:
6759 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6760 case OMPScheduleType::BaseStaticBalancedChunked:
6761 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6762 NeedsBarrier, ChunkSize);
6763
6764 default:
6765 llvm_unreachable("Unknown/unimplemented schedule kind");
6766 }
6767}
6768
6769/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6770/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6771/// the runtime. Always interpret integers as unsigned similarly to
6772/// CanonicalLoopInfo.
6773static FunctionCallee
6775 unsigned Bitwidth = Ty->getIntegerBitWidth();
6776 if (Bitwidth == 32)
6777 return OMPBuilder.getOrCreateRuntimeFunction(
6778 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6779 if (Bitwidth == 64)
6780 return OMPBuilder.getOrCreateRuntimeFunction(
6781 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6782 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6783}
6784
6785/// Returns an LLVM function to call for updating the next loop using OpenMP
6786/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6787/// the runtime. Always interpret integers as unsigned similarly to
6788/// CanonicalLoopInfo.
6789static FunctionCallee
6791 unsigned Bitwidth = Ty->getIntegerBitWidth();
6792 if (Bitwidth == 32)
6793 return OMPBuilder.getOrCreateRuntimeFunction(
6794 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6795 if (Bitwidth == 64)
6796 return OMPBuilder.getOrCreateRuntimeFunction(
6797 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6798 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6799}
6800
6801/// Returns an LLVM function to call for finalizing the dynamic loop using
6802/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6803/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6804static FunctionCallee
6806 unsigned Bitwidth = Ty->getIntegerBitWidth();
6807 if (Bitwidth == 32)
6808 return OMPBuilder.getOrCreateRuntimeFunction(
6809 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6810 if (Bitwidth == 64)
6811 return OMPBuilder.getOrCreateRuntimeFunction(
6812 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6813 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6814}
6815
6817OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6818 InsertPointTy AllocaIP,
6819 OMPScheduleType SchedType,
6820 bool NeedsBarrier, Value *Chunk) {
6821 assert(CLI->isValid() && "Requires a valid canonical loop");
6822 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6823 "Require dedicated allocate IP");
6825 "Require valid schedule type");
6826
6827 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6828 OMPScheduleType::ModifierOrdered;
6829
6830 // Set up the source location value for OpenMP runtime.
6831 Builder.SetCurrentDebugLocation(DL);
6832
6833 uint32_t SrcLocStrSize;
6834 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6835 Value *SrcLoc =
6836 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6837
6838 // Declare useful OpenMP runtime functions.
6839 Value *IV = CLI->getIndVar();
6840 Type *IVTy = IV->getType();
6841 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6842 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6843
6844 // Allocate space for computed loop bounds as expected by the "init" function.
6845 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6846 Type *I32Type = Type::getInt32Ty(M.getContext());
6847 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6848 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6849 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6850 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6851 CLI->setLastIter(PLastIter);
6852
6853 // At the end of the preheader, prepare for calling the "init" function by
6854 // storing the current loop bounds into the allocated space. A canonical loop
6855 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6856 // and produces an inclusive upper bound.
6857 BasicBlock *PreHeader = CLI->getPreheader();
6858 Builder.SetInsertPoint(PreHeader->getTerminator());
6859 Constant *One = ConstantInt::get(IVTy, 1);
6860 Builder.CreateStore(One, PLowerBound);
6861 Value *UpperBound = CLI->getTripCount();
6862 Builder.CreateStore(UpperBound, PUpperBound);
6863 Builder.CreateStore(One, PStride);
6864
6865 BasicBlock *Header = CLI->getHeader();
6866 BasicBlock *Exit = CLI->getExit();
6867 BasicBlock *Cond = CLI->getCond();
6868 BasicBlock *Latch = CLI->getLatch();
6869 InsertPointTy AfterIP = CLI->getAfterIP();
6870
6871 // The CLI will be "broken" in the code below, as the loop is no longer
6872 // a valid canonical loop.
6873
6874 if (!Chunk)
6875 Chunk = One;
6876
6877 Value *ThreadNum =
6878 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6879
6880 Constant *SchedulingType =
6881 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6882
6883 // Call the "init" function.
6884 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6885 /* LowerBound */ One, UpperBound,
6886 /* step */ One, Chunk});
6887
6888 // An outer loop around the existing one.
6889 BasicBlock *OuterCond = BasicBlock::Create(
6890 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6891 PreHeader->getParent());
6892 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6893 Builder.SetInsertPoint(OuterCond, OuterCond->getFirstInsertionPt());
6895 DynamicNext,
6896 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6897 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6898 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6899 Value *LowerBound =
6900 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6901 Builder.CreateCondBr(MoreWork, Header, Exit);
6902
6903 // Change PHI-node in loop header to use outer cond rather than preheader,
6904 // and set IV to the LowerBound.
6905 Instruction *Phi = &Header->front();
6906 auto *PI = cast<PHINode>(Phi);
6907 PI->setIncomingBlock(0, OuterCond);
6908 PI->setIncomingValue(0, LowerBound);
6909
6910 // Then set the pre-header to jump to the OuterCond
6911 Instruction *Term = PreHeader->getTerminator();
6912 auto *Br = cast<UncondBrInst>(Term);
6913 Br->setSuccessor(OuterCond);
6914
6915 // Modify the inner condition:
6916 // * Use the UpperBound returned from the DynamicNext call.
6917 // * jump to the loop outer loop when done with one of the inner loops.
6918 Builder.SetInsertPoint(Cond, Cond->getFirstInsertionPt());
6919 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
6920 Instruction *Comp = &*Builder.GetInsertPoint();
6921 auto *CI = cast<CmpInst>(Comp);
6922 CI->setOperand(1, UpperBound);
6923 // Redirect the inner exit to branch to outer condition.
6924 Instruction *Branch = &Cond->back();
6925 auto *BI = cast<CondBrInst>(Branch);
6926 assert(BI->getSuccessor(1) == Exit);
6927 BI->setSuccessor(1, OuterCond);
6928
6929 // Call the "fini" function if "ordered" is present in wsloop directive.
6930 if (Ordered) {
6931 Builder.SetInsertPoint(&Latch->back());
6932 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
6933 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
6934 }
6935
6936 // Add the barrier if requested.
6937 if (NeedsBarrier) {
6938 Builder.SetInsertPoint(&Exit->back());
6939 InsertPointOrErrorTy BarrierIP =
6941 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6942 /* CheckCancelFlag */ false);
6943 if (!BarrierIP)
6944 return BarrierIP.takeError();
6945 }
6946
6947 CLI->invalidate();
6948 return AfterIP;
6949}
6950
6951/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
6952/// after this \p OldTarget will be orphaned.
6954 BasicBlock *NewTarget, DebugLoc DL) {
6955 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
6956 redirectTo(Pred, NewTarget, DL);
6957}
6958
6960 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
6961 // We add a block to BBsToKeep iff we have proven it has an external use.
6963
6964 while (true) {
6965 bool Changed = false;
6966
6967 for (BasicBlock *BB : BBs) {
6968 if (BBsToKeep.contains(BB))
6969 continue;
6970
6971 for (Use &U : BB->uses()) {
6972 auto *UseInst = dyn_cast<Instruction>(U.getUser());
6973 if (!UseInst)
6974 continue;
6975 BasicBlock *UseBB = UseInst->getParent();
6976 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
6977 BBsToKeep.insert(BB);
6978 Changed = true;
6979 break;
6980 }
6981 }
6982 }
6983
6984 if (!Changed)
6985 break;
6986 }
6987
6989 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
6990 DeleteDeadBlocks(BBsToDelete);
6991}
6992
6993CanonicalLoopInfo *
6995 InsertPointTy ComputeIP) {
6996 assert(Loops.size() >= 1 && "At least one loop required");
6997 size_t NumLoops = Loops.size();
6998
6999 // Nothing to do if there is already just one loop.
7000 if (NumLoops == 1)
7001 return Loops.front();
7002
7003 CanonicalLoopInfo *Outermost = Loops.front();
7004 CanonicalLoopInfo *Innermost = Loops.back();
7005 BasicBlock *OrigPreheader = Outermost->getPreheader();
7006 BasicBlock *OrigAfter = Outermost->getAfter();
7007 Function *F = OrigPreheader->getParent();
7008
7009 // Loop control blocks that may become orphaned later.
7010 SmallVector<BasicBlock *, 12> OldControlBBs;
7011 OldControlBBs.reserve(6 * Loops.size());
7013 Loop->collectControlBlocks(OldControlBBs);
7014
7015 // Setup the IRBuilder for inserting the trip count computation.
7016 Builder.SetCurrentDebugLocation(DL);
7017 if (ComputeIP.isSet())
7018 Builder.restoreIP(ComputeIP);
7019 else
7020 Builder.restoreIP(Outermost->getPreheaderIP());
7021
7022 // Derive the collapsed' loop trip count.
7023 // TODO: Find common/largest indvar type.
7024 Value *CollapsedTripCount = nullptr;
7025 for (CanonicalLoopInfo *L : Loops) {
7026 assert(L->isValid() &&
7027 "All loops to collapse must be valid canonical loops");
7028 Value *OrigTripCount = L->getTripCount();
7029 if (!CollapsedTripCount) {
7030 CollapsedTripCount = OrigTripCount;
7031 continue;
7032 }
7033
7034 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7035 CollapsedTripCount =
7036 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7037 }
7038
7039 // Create the collapsed loop control flow.
7040 CanonicalLoopInfo *Result =
7041 createLoopSkeleton(DL, CollapsedTripCount, F,
7042 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7043 /*IsCollapsed=*/true);
7044
7045 // Build the collapsed loop body code.
7046 // Start with deriving the input loop induction variables from the collapsed
7047 // one, using a divmod scheme. To preserve the original loops' order, the
7048 // innermost loop use the least significant bits.
7049 Builder.restoreIP(Result->getBodyIP());
7050
7051 Value *Leftover = Result->getIndVar();
7052 SmallVector<Value *> NewIndVars;
7053 NewIndVars.resize(NumLoops);
7054 for (int i = NumLoops - 1; i >= 1; --i) {
7055 Value *OrigTripCount = Loops[i]->getTripCount();
7056
7057 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7058 NewIndVars[i] = NewIndVar;
7059
7060 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7061 }
7062 // Outermost loop gets all the remaining bits.
7063 NewIndVars[0] = Leftover;
7064
7065 // Construct the loop body control flow.
7066 // We progressively construct the branch structure following in direction of
7067 // the control flow, from the leading in-between code, the loop nest body, the
7068 // trailing in-between code, and rejoining the collapsed loop's latch.
7069 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7070 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7071 // its predecessors as sources.
7072 BasicBlock *ContinueBlock = Result->getBody();
7073 BasicBlock *ContinuePred = nullptr;
7074 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7075 BasicBlock *NextSrc) {
7076 if (ContinueBlock)
7077 redirectTo(ContinueBlock, Dest, DL);
7078 else
7079 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7080
7081 ContinueBlock = nullptr;
7082 ContinuePred = NextSrc;
7083 };
7084
7085 // The code before the nested loop of each level.
7086 // Because we are sinking it into the nest, it will be executed more often
7087 // that the original loop. More sophisticated schemes could keep track of what
7088 // the in-between code is and instantiate it only once per thread.
7089 for (size_t i = 0; i < NumLoops - 1; ++i)
7090 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7091
7092 // Connect the loop nest body.
7093 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7094
7095 // The code after the nested loop at each level.
7096 for (size_t i = NumLoops - 1; i > 0; --i)
7097 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7098
7099 // Connect the finished loop to the collapsed loop latch.
7100 ContinueWith(Result->getLatch(), nullptr);
7101
7102 // Replace the input loops with the new collapsed loop.
7103 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7104 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7105
7106 // Replace the input loop indvars with the derived ones.
7107 for (size_t i = 0; i < NumLoops; ++i)
7108 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7109
7110 // Remove unused parts of the input loops.
7111 removeUnusedBlocksFromParent(OldControlBBs);
7112
7113 for (CanonicalLoopInfo *L : Loops)
7114 L->invalidate();
7115
7116#ifndef NDEBUG
7117 Result->assertOK();
7118#endif
7119 return Result;
7120}
7121
7122std::vector<CanonicalLoopInfo *>
7124 ArrayRef<Value *> TileSizes) {
7125 assert(TileSizes.size() == Loops.size() &&
7126 "Must pass as many tile sizes as there are loops");
7127 int NumLoops = Loops.size();
7128 assert(NumLoops >= 1 && "At least one loop to tile required");
7129
7130 CanonicalLoopInfo *OutermostLoop = Loops.front();
7131 CanonicalLoopInfo *InnermostLoop = Loops.back();
7132 Function *F = OutermostLoop->getBody()->getParent();
7133 BasicBlock *InnerEnter = InnermostLoop->getBody();
7134 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7135
7136 // Loop control blocks that may become orphaned later.
7137 SmallVector<BasicBlock *, 12> OldControlBBs;
7138 OldControlBBs.reserve(6 * Loops.size());
7140 Loop->collectControlBlocks(OldControlBBs);
7141
7142 // Collect original trip counts and induction variable to be accessible by
7143 // index. Also, the structure of the original loops is not preserved during
7144 // the construction of the tiled loops, so do it before we scavenge the BBs of
7145 // any original CanonicalLoopInfo.
7146 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7147 for (CanonicalLoopInfo *L : Loops) {
7148 assert(L->isValid() && "All input loops must be valid canonical loops");
7149 OrigTripCounts.push_back(L->getTripCount());
7150 OrigIndVars.push_back(L->getIndVar());
7151 }
7152
7153 // Collect the code between loop headers. These may contain SSA definitions
7154 // that are used in the loop nest body. To be usable with in the innermost
7155 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7156 // these instructions may be executed more often than before the tiling.
7157 // TODO: It would be sufficient to only sink them into body of the
7158 // corresponding tile loop.
7160 for (int i = 0; i < NumLoops - 1; ++i) {
7161 CanonicalLoopInfo *Surrounding = Loops[i];
7162 CanonicalLoopInfo *Nested = Loops[i + 1];
7163
7164 BasicBlock *EnterBB = Surrounding->getBody();
7165 BasicBlock *ExitBB = Nested->getHeader();
7166 InbetweenCode.emplace_back(EnterBB, ExitBB);
7167 }
7168
7169 // Compute the trip counts of the floor loops.
7170 Builder.SetCurrentDebugLocation(DL);
7171 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7172 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7173 for (int i = 0; i < NumLoops; ++i) {
7174 Value *TileSize = TileSizes[i];
7175 Value *OrigTripCount = OrigTripCounts[i];
7176 Type *IVType = OrigTripCount->getType();
7177
7178 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7179 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7180
7181 // 0 if tripcount divides the tilesize, 1 otherwise.
7182 // 1 means we need an additional iteration for a partial tile.
7183 //
7184 // Unfortunately we cannot just use the roundup-formula
7185 // (tripcount + tilesize - 1)/tilesize
7186 // because the summation might overflow. We do not want introduce undefined
7187 // behavior when the untiled loop nest did not.
7188 Value *FloorTripOverflow =
7189 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7190
7191 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7192 Value *FloorTripCount =
7193 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7194 "omp_floor" + Twine(i) + ".tripcount", true);
7195
7196 // Remember some values for later use.
7197 FloorCompleteCount.push_back(FloorCompleteTripCount);
7198 FloorCount.push_back(FloorTripCount);
7199 FloorRems.push_back(FloorTripRem);
7200 }
7201
7202 // Generate the new loop nest, from the outermost to the innermost.
7203 std::vector<CanonicalLoopInfo *> Result;
7204 Result.reserve(NumLoops * 2);
7205
7206 // The basic block of the surrounding loop that enters the nest generated
7207 // loop.
7208 BasicBlock *Enter = OutermostLoop->getPreheader();
7209
7210 // The basic block of the surrounding loop where the inner code should
7211 // continue.
7212 BasicBlock *Continue = OutermostLoop->getAfter();
7213
7214 // Where the next loop basic block should be inserted.
7215 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7216
7217 auto EmbeddNewLoop =
7218 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7219 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7220 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7221 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7222 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7223 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7224
7225 // Setup the position where the next embedded loop connects to this loop.
7226 Enter = EmbeddedLoop->getBody();
7227 Continue = EmbeddedLoop->getLatch();
7228 OutroInsertBefore = EmbeddedLoop->getLatch();
7229 return EmbeddedLoop;
7230 };
7231
7232 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7233 const Twine &NameBase) {
7234 for (auto P : enumerate(TripCounts)) {
7235 CanonicalLoopInfo *EmbeddedLoop =
7236 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7237 Result.push_back(EmbeddedLoop);
7238 }
7239 };
7240
7241 EmbeddNewLoops(FloorCount, "floor");
7242
7243 // Within the innermost floor loop, emit the code that computes the tile
7244 // sizes.
7245 Builder.SetInsertPoint(Enter->getTerminator());
7246 SmallVector<Value *, 4> TileCounts;
7247 for (int i = 0; i < NumLoops; ++i) {
7248 CanonicalLoopInfo *FloorLoop = Result[i];
7249 Value *TileSize = TileSizes[i];
7250
7251 Value *FloorIsEpilogue =
7252 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7253 Value *TileTripCount =
7254 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7255
7256 TileCounts.push_back(TileTripCount);
7257 }
7258
7259 // Create the tile loops.
7260 EmbeddNewLoops(TileCounts, "tile");
7261
7262 // Insert the inbetween code into the body.
7263 BasicBlock *BodyEnter = Enter;
7264 BasicBlock *BodyEntered = nullptr;
7265 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7266 BasicBlock *EnterBB = P.first;
7267 BasicBlock *ExitBB = P.second;
7268
7269 if (BodyEnter)
7270 redirectTo(BodyEnter, EnterBB, DL);
7271 else
7272 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7273
7274 BodyEnter = nullptr;
7275 BodyEntered = ExitBB;
7276 }
7277
7278 // Append the original loop nest body into the generated loop nest body.
7279 if (BodyEnter)
7280 redirectTo(BodyEnter, InnerEnter, DL);
7281 else
7282 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7284
7285 // Replace the original induction variable with an induction variable computed
7286 // from the tile and floor induction variables.
7287 Builder.restoreIP(Result.back()->getBodyIP());
7288 for (int i = 0; i < NumLoops; ++i) {
7289 CanonicalLoopInfo *FloorLoop = Result[i];
7290 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7291 Value *OrigIndVar = OrigIndVars[i];
7292 Value *Size = TileSizes[i];
7293
7294 Value *Scale =
7295 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7296 Value *Shift =
7297 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7298 OrigIndVar->replaceAllUsesWith(Shift);
7299 }
7300
7301 // Remove unused parts of the original loops.
7302 removeUnusedBlocksFromParent(OldControlBBs);
7303
7304 for (CanonicalLoopInfo *L : Loops)
7305 L->invalidate();
7306
7307#ifndef NDEBUG
7308 for (CanonicalLoopInfo *GenL : Result)
7309 GenL->assertOK();
7310#endif
7311 return Result;
7312}
7313
7314/// Attach metadata \p Properties to the basic block described by \p BB. If the
7315/// basic block already has metadata, the basic block properties are appended.
7318 // Nothing to do if no property to attach.
7319 if (Properties.empty())
7320 return;
7321
7322 LLVMContext &Ctx = BB->getContext();
7323 SmallVector<Metadata *> NewProperties;
7324 NewProperties.push_back(nullptr);
7325
7326 // If the basic block already has metadata, prepend it to the new metadata.
7327 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7328 if (Existing)
7329 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7330
7331 append_range(NewProperties, Properties);
7332 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7333 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7334
7335 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7336}
7337
7338/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7339/// loop already has metadata, the loop properties are appended.
7342 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7343
7344 // Attach metadata to the loop's latch
7345 BasicBlock *Latch = Loop->getLatch();
7346 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7348}
7349
7350/// Attach llvm.access.group metadata to the memref instructions of \p Block
7352 LoopInfo &LI) {
7353 for (Instruction &I : *Block) {
7354 if (I.mayReadOrWriteMemory()) {
7355 // TODO: This instruction may already have access group from
7356 // other pragmas e.g. #pragma clang loop vectorize. Append
7357 // so that the existing metadata is not overwritten.
7358 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7359 }
7360 }
7361}
7362
7363CanonicalLoopInfo *
7365 CanonicalLoopInfo *firstLoop = Loops.front();
7366 CanonicalLoopInfo *lastLoop = Loops.back();
7367 Function *F = firstLoop->getPreheader()->getParent();
7368
7369 // Loop control blocks that will become orphaned later
7370 SmallVector<BasicBlock *> oldControlBBs;
7372 Loop->collectControlBlocks(oldControlBBs);
7373
7374 // Collect original trip counts
7375 SmallVector<Value *> origTripCounts;
7376 for (CanonicalLoopInfo *L : Loops) {
7377 assert(L->isValid() && "All input loops must be valid canonical loops");
7378 origTripCounts.push_back(L->getTripCount());
7379 }
7380
7381 Builder.SetCurrentDebugLocation(DL);
7382
7383 // Compute max trip count.
7384 // The fused loop will be from 0 to max(origTripCounts)
7385 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7386 F, firstLoop->getHeader());
7387 Builder.SetInsertPoint(TCBlock);
7388 Value *fusedTripCount = nullptr;
7389 for (CanonicalLoopInfo *L : Loops) {
7390 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7391 Value *origTripCount = L->getTripCount();
7392 if (!fusedTripCount) {
7393 fusedTripCount = origTripCount;
7394 continue;
7395 }
7396 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7397 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7398 ".omp.fuse.tc");
7399 }
7400
7401 // Generate new loop
7402 CanonicalLoopInfo *fused =
7403 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7404 lastLoop->getLatch(), "fused");
7405
7406 // Replace original loops with the fused loop
7407 // Preheader and After are not considered inside the CLI.
7408 // These are used to compute the individual TCs of the loops
7409 // so they have to be put before the resulting fused loop.
7410 // Moving them up for readability.
7411 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7412 Loops[i]->getPreheader()->moveBefore(TCBlock);
7413 Loops[i]->getAfter()->moveBefore(TCBlock);
7414 }
7415 lastLoop->getPreheader()->moveBefore(TCBlock);
7416
7417 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7418 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7419 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7420 }
7421 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7422 redirectTo(TCBlock, fused->getPreheader(), DL);
7423 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7424
7425 // Build the fused body
7426 // Create new Blocks with conditions that jump to the original loop bodies
7428 SmallVector<Value *> condValues;
7429 for (size_t i = 0; i < Loops.size(); ++i) {
7430 BasicBlock *condBlock = BasicBlock::Create(
7431 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7432 Builder.SetInsertPoint(condBlock);
7433 Value *condValue =
7434 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7435 condBBs.push_back(condBlock);
7436 condValues.push_back(condValue);
7437 }
7438 // Join the condition blocks with the bodies of the original loops
7439 redirectTo(fused->getBody(), condBBs[0], DL);
7440 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7441 Builder.SetInsertPoint(condBBs[i]);
7442 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7443 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7444 // Replace the IV with the fused IV
7445 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7446 }
7447 // Last body jumps to the created end body block
7448 Builder.SetInsertPoint(condBBs.back());
7449 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7450 fused->getLatch());
7451 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7452 // Replace the IV with the fused IV
7453 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7454
7455 // The loop latch must have only one predecessor. Currently it is branched to
7456 // from both the last condition block and the last loop body
7457 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7458 "omp.fused.pre_latch");
7459
7460 // Remove unused parts
7461 removeUnusedBlocksFromParent(oldControlBBs);
7462
7463 // Invalidate old CLIs
7464 for (CanonicalLoopInfo *L : Loops)
7465 L->invalidate();
7466
7467#ifndef NDEBUG
7468 fused->assertOK();
7469#endif
7470 return fused;
7471}
7472
7474 LLVMContext &Ctx = Builder.getContext();
7476 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7477 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7478}
7479
7481 LLVMContext &Ctx = Builder.getContext();
7483 Loop, {
7484 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7485 });
7486}
7487
7488void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7489 Value *IfCond, ValueToValueMapTy &VMap,
7490 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7491 const Twine &NamePrefix) {
7492 Function *F = CanonicalLoop->getFunction();
7493
7494 // We can't do
7495 // if (cond) {
7496 // simd_loop;
7497 // } else {
7498 // non_simd_loop;
7499 // }
7500 // because then the CanonicalLoopInfo would only point to one of the loops:
7501 // leading to other constructs operating on the same loop to malfunction.
7502 // Instead generate
7503 // while (...) {
7504 // if (cond) {
7505 // simd_body;
7506 // } else {
7507 // not_simd_body;
7508 // }
7509 // }
7510 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7511 // body at -O3
7512
7513 // Define where if branch should be inserted
7514 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7515
7516 // Create additional blocks for the if statement
7517 BasicBlock *Cond = SplitBeforeIt->getParent();
7518 llvm::LLVMContext &C = Cond->getContext();
7520 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7522 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7523
7524 // Create if condition branch.
7525 Builder.SetInsertPoint(SplitBeforeIt);
7526 Instruction *BrInstr =
7527 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7528 InsertPointTy IP{BrInstr->getParent(), ++BrInstr->getIterator()};
7529 // Then block contains branch to omp loop body which needs to be vectorized
7530 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7531 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7532
7533 Builder.SetInsertPoint(ElseBlock);
7534
7535 // Clone loop for the else branch
7537
7538 SmallVector<BasicBlock *, 8> ExistingBlocks;
7539 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7540 ExistingBlocks.push_back(ThenBlock);
7541 ExistingBlocks.append(L->block_begin(), L->block_end());
7542 // Cond is the block that has the if clause condition
7543 // LoopCond is omp_loop.cond
7544 // LoopHeader is omp_loop.header
7545 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7546 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7547 assert(LoopCond && LoopHeader && "Invalid loop structure");
7548 for (BasicBlock *Block : ExistingBlocks) {
7549 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7550 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7551 continue;
7552 }
7553 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7554
7555 // fix name not to be omp.if.then
7556 if (Block == ThenBlock)
7557 NewBB->setName(NamePrefix + ".if.else");
7558
7559 NewBB->moveBefore(CanonicalLoop->getExit());
7560 VMap[Block] = NewBB;
7561 NewBlocks.push_back(NewBB);
7562 }
7563 remapInstructionsInBlocks(NewBlocks, VMap);
7564 Builder.CreateBr(NewBlocks.front());
7565
7566 // The loop latch must have only one predecessor. Currently it is branched to
7567 // from both the 'then' and 'else' branches.
7568 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7569 NamePrefix + ".pre_latch");
7570
7571 // Ensure that the then block is added to the loop so we add the attributes in
7572 // the next step
7573 L->addBasicBlockToLoop(ThenBlock, LI);
7574}
7575
7576unsigned
7578 const StringMap<bool> &Features) {
7579 if (TargetTriple.isX86()) {
7580 if (Features.lookup("avx512f"))
7581 return 512;
7582 else if (Features.lookup("avx"))
7583 return 256;
7584 return 128;
7585 }
7586 if (TargetTriple.isPPC())
7587 return 128;
7588 if (TargetTriple.isWasm())
7589 return 128;
7590 if (TargetTriple.isSystemZ())
7591 return 64;
7592 return 0;
7593}
7594
7596 MapVector<Value *, Value *> AlignedVars,
7597 Value *IfCond, OrderKind Order,
7598 ConstantInt *Simdlen, ConstantInt *Safelen) {
7599 LLVMContext &Ctx = Builder.getContext();
7600
7601 Function *F = CanonicalLoop->getFunction();
7602
7603 // Blocks must have terminators.
7604 // FIXME: Don't run analyses on incomplete/invalid IR.
7606 for (BasicBlock &BB : *F)
7607 if (!BB.hasTerminator())
7608 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7609
7610 // TODO: We should not rely on pass manager. Currently we use pass manager
7611 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7612 // object. We should have a method which returns all blocks between
7613 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7615 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7616 FAM.registerPass([]() { return LoopAnalysis(); });
7617 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7618
7619 LoopAnalysis LIA;
7620 LoopInfo &&LI = LIA.run(*F, FAM);
7621
7622 for (Instruction *I : UIs)
7623 I->eraseFromParent();
7624
7625 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7626 if (AlignedVars.size()) {
7627 InsertPointTy IP = Builder.saveIP();
7628 for (auto &AlignedItem : AlignedVars) {
7629 Value *AlignedPtr = AlignedItem.first;
7630 Value *Alignment = AlignedItem.second;
7631 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7632 Builder.SetInsertPoint(loadInst->getNextNode());
7633 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7634 Alignment);
7635 }
7636 Builder.restoreIP(IP);
7637 }
7638
7639 if (IfCond) {
7640 ValueToValueMapTy VMap;
7641 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7642 }
7643
7645
7646 // Get the basic blocks from the loop in which memref instructions
7647 // can be found.
7648 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7649 // preferably without running any passes.
7650 for (BasicBlock *Block : L->getBlocks()) {
7651 if (Block == CanonicalLoop->getCond() ||
7652 Block == CanonicalLoop->getHeader())
7653 continue;
7654 Reachable.insert(Block);
7655 }
7656
7657 SmallVector<Metadata *> LoopMDList;
7658
7659 // In presence of finite 'safelen', it may be unsafe to mark all
7660 // the memory instructions parallel, because loop-carried
7661 // dependences of 'safelen' iterations are possible.
7662 // If clause order(concurrent) is specified then the memory instructions
7663 // are marked parallel even if 'safelen' is finite.
7664 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7665 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7666
7667 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7668 // versions so we can't add the loop attributes in that case.
7669 if (IfCond) {
7670 // we can still add llvm.loop.parallel_access
7671 addLoopMetadata(CanonicalLoop, LoopMDList);
7672 return;
7673 }
7674
7675 // Use the above access group metadata to create loop level
7676 // metadata, which should be distinct for each loop.
7677 LoopMDList.push_back(
7678 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7679
7680 if (Simdlen || Safelen) {
7681 // If both simdlen and safelen clauses are specified, the value of the
7682 // simdlen parameter must be less than or equal to the value of the safelen
7683 // parameter. Therefore, use safelen only in the absence of simdlen.
7684 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7685 LoopMDList.push_back(
7686 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7687 ConstantAsMetadata::get(VectorizeWidth)}));
7688 }
7689
7690 addLoopMetadata(CanonicalLoop, LoopMDList);
7691}
7692
7693/// Create the TargetMachine object to query the backend for optimization
7694/// preferences.
7695///
7696/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7697/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7698/// needed for the LLVM pass pipline. We use some default options to avoid
7699/// having to pass too many settings from the frontend that probably do not
7700/// matter.
7701///
7702/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7703/// method. If we are going to use TargetMachine for more purposes, especially
7704/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7705/// might become be worth requiring front-ends to pass on their TargetMachine,
7706/// or at least cache it between methods. Note that while fontends such as Clang
7707/// have just a single main TargetMachine per translation unit, "target-cpu" and
7708/// "target-features" that determine the TargetMachine are per-function and can
7709/// be overrided using __attribute__((target("OPTIONS"))).
7710static std::unique_ptr<TargetMachine>
7712 Module *M = F->getParent();
7713
7714 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7715 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7716 const llvm::Triple &Triple = M->getTargetTriple();
7717
7718 std::string Error;
7720 if (!TheTarget)
7721 return {};
7722
7724 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7725 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7726 /*CodeModel=*/std::nullopt, OptLevel));
7727}
7728
7729/// Heuristically determine the best-performant unroll factor for \p CLI. This
7730/// depends on the target processor. We are re-using the same heuristics as the
7731/// LoopUnrollPass.
7733 Function *F = CLI->getFunction();
7734
7735 // Assume the user requests the most aggressive unrolling, even if the rest of
7736 // the code is optimized using a lower setting.
7738 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7739
7740 // Blocks must have terminators.
7741 // FIXME: Don't run analyses on incomplete/invalid IR.
7743 for (BasicBlock &BB : *F)
7744 if (!BB.hasTerminator())
7745 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7746
7748 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7749 FAM.registerPass([]() { return AssumptionAnalysis(); });
7750 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7751 FAM.registerPass([]() { return LoopAnalysis(); });
7752 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7753 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7754 TargetIRAnalysis TIRA;
7755 if (TM)
7756 TIRA = TargetIRAnalysis(
7757 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7758 FAM.registerPass([&]() { return TIRA; });
7759
7760 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7762 ScalarEvolution &&SE = SEA.run(*F, FAM);
7764 DominatorTree &&DT = DTA.run(*F, FAM);
7765 LoopAnalysis LIA;
7766 LoopInfo &&LI = LIA.run(*F, FAM);
7768 AssumptionCache &&AC = ACT.run(*F, FAM);
7770
7771 for (Instruction *I : UIs)
7772 I->eraseFromParent();
7773
7774 Loop *L = LI.getLoopFor(CLI->getHeader());
7775 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7776
7778 L, SE, TTI,
7779 /*BlockFrequencyInfo=*/nullptr,
7780 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7781 /*UserThreshold=*/std::nullopt,
7782 /*UserAllowPartial=*/true,
7783 /*UserAllowRuntime=*/true,
7784 /*UserUpperBound=*/std::nullopt,
7785 /*UserFullUnrollMaxCount=*/std::nullopt);
7786
7787 UP.Force = true;
7788
7789 // Account for additional optimizations taking place before the LoopUnrollPass
7790 // would unroll the loop.
7793
7794 // Use normal unroll factors even if the rest of the code is optimized for
7795 // size.
7798
7799 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7800 << " Threshold=" << UP.Threshold << "\n"
7801 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7802 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7803 << " PartialOptSizeThreshold="
7804 << UP.PartialOptSizeThreshold << "\n");
7805
7806 // Disable peeling.
7809 /*UserAllowPeeling=*/false,
7810 /*UserAllowProfileBasedPeeling=*/false,
7811 /*UnrollingSpecficValues=*/false);
7812
7814 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7815
7816 // Assume that reads and writes to stack variables can be eliminated by
7817 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7818 // size.
7819 for (BasicBlock *BB : L->blocks()) {
7820 for (Instruction &I : *BB) {
7821 Value *Ptr;
7822 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7823 Ptr = Load->getPointerOperand();
7824 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7825 Ptr = Store->getPointerOperand();
7826 } else
7827 continue;
7828
7829 Ptr = Ptr->stripPointerCasts();
7830
7831 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7832 if (Alloca->getParent() == &F->getEntryBlock())
7833 EphValues.insert(&I);
7834 }
7835 }
7836 }
7837
7838 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7839
7840 // Loop is not unrollable if the loop contains certain instructions.
7841 if (!UCE.canUnroll()) {
7842 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7843 return 1;
7844 }
7845
7846 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7847 << "\n");
7848
7849 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7850 // be able to use it.
7851 int TripCount = 0;
7852 int MaxTripCount = 0;
7853 bool MaxOrZero = false;
7854 unsigned TripMultiple = 0;
7855
7856 unsigned Factor =
7857 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7858 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7859 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7860
7861 // This function returns 1 to signal to not unroll a loop.
7862 if (Factor == 0)
7863 return 1;
7864 return Factor;
7865}
7866
7868 int32_t Factor,
7869 CanonicalLoopInfo **UnrolledCLI) {
7870 assert(Factor >= 0 && "Unroll factor must not be negative");
7871
7872 Function *F = Loop->getFunction();
7873 LLVMContext &Ctx = F->getContext();
7874
7875 // If the unrolled loop is not used for another loop-associated directive, it
7876 // is sufficient to add metadata for the LoopUnrollPass.
7877 if (!UnrolledCLI) {
7878 SmallVector<Metadata *, 2> LoopMetadata;
7879 LoopMetadata.push_back(
7880 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7881
7882 if (Factor >= 1) {
7884 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7885 LoopMetadata.push_back(MDNode::get(
7886 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7887 }
7888
7889 addLoopMetadata(Loop, LoopMetadata);
7890 return;
7891 }
7892
7893 // Heuristically determine the unroll factor.
7894 if (Factor == 0)
7896
7897 // No change required with unroll factor 1.
7898 if (Factor == 1) {
7899 *UnrolledCLI = Loop;
7900 return;
7901 }
7902
7903 assert(Factor >= 2 &&
7904 "unrolling only makes sense with a factor of 2 or larger");
7905
7906 Type *IndVarTy = Loop->getIndVarType();
7907
7908 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7909 // unroll the inner loop.
7910 Value *FactorVal =
7911 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7912 /*isSigned=*/false));
7913 std::vector<CanonicalLoopInfo *> LoopNest =
7914 tileLoops(DL, {Loop}, {FactorVal});
7915 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7916 *UnrolledCLI = LoopNest[0];
7917 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7918
7919 // LoopUnrollPass can only fully unroll loops with constant trip count.
7920 // Unroll by the unroll factor with a fallback epilog for the remainder
7921 // iterations if necessary.
7923 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7925 InnerLoop,
7926 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7928 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
7929
7930#ifndef NDEBUG
7931 (*UnrolledCLI)->assertOK();
7932#endif
7933}
7934
7937 llvm::Value *BufSize, llvm::Value *CpyBuf,
7938 llvm::Value *CpyFn, llvm::Value *DidIt) {
7939 if (!updateToLocation(Loc))
7940 return Loc.IP;
7941
7942 uint32_t SrcLocStrSize;
7943 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7944 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7945 Value *ThreadId = getOrCreateThreadID(Ident);
7946
7947 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
7948
7949 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
7950
7951 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
7952 createRuntimeFunctionCall(Fn, Args);
7953
7954 return Builder.saveIP();
7955}
7956
7958 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7959 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
7961
7962 if (!updateToLocation(Loc))
7963 return Loc.IP;
7964
7965 // If needed allocate and initialize `DidIt` with 0.
7966 // DidIt: flag variable: 1=single thread; 0=not single thread.
7967 llvm::Value *DidIt = nullptr;
7968 if (!CPVars.empty()) {
7969 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
7970 Builder.CreateStore(Builder.getInt32(0), DidIt);
7971 }
7972
7973 Directive OMPD = Directive::OMPD_single;
7974 uint32_t SrcLocStrSize;
7975 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7976 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7977 Value *ThreadId = getOrCreateThreadID(Ident);
7978 Value *Args[] = {Ident, ThreadId};
7979
7980 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
7981 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
7982
7983 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
7984 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
7985
7986 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
7987 if (Error Err = FiniCB(IP))
7988 return Err;
7989
7990 // The thread that executes the single region must set `DidIt` to 1.
7991 // This is used by __kmpc_copyprivate, to know if the caller is the
7992 // single thread or not.
7993 if (DidIt)
7994 Builder.CreateStore(Builder.getInt32(1), DidIt);
7995
7996 return Error::success();
7997 };
7998
7999 // generates the following:
8000 // if (__kmpc_single()) {
8001 // .... single region ...
8002 // __kmpc_end_single
8003 // }
8004 // __kmpc_copyprivate
8005 // __kmpc_barrier
8006
8007 InsertPointOrErrorTy AfterIP =
8008 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8009 /*Conditional*/ true,
8010 /*hasFinalize*/ true);
8011 if (!AfterIP)
8012 return AfterIP.takeError();
8013
8014 if (DidIt) {
8015 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8016 // NOTE BufSize is currently unused, so just pass 0.
8018 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8019 CPFuncs[I], DidIt);
8020 // NOTE __kmpc_copyprivate already inserts a barrier
8021 } else if (!IsNowait) {
8022 InsertPointOrErrorTy AfterIP =
8024 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8025 /* CheckCancelFlag */ false);
8026 if (!AfterIP)
8027 return AfterIP.takeError();
8028 }
8029 return Builder.saveIP();
8030}
8031
8034 BodyGenCallbackTy BodyGenCB,
8035 FinalizeCallbackTy FiniCB, bool IsNowait) {
8036
8037 if (!updateToLocation(Loc))
8038 return Loc.IP;
8039
8040 // All threads execute the scope body — no conditional entry.
8041 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8042 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8043 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8044 /*IsCancellable=*/false);
8045 if (!AfterIP)
8046 return AfterIP.takeError();
8047
8048 Builder.restoreIP(*AfterIP);
8049 if (!IsNowait) {
8050 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8051 omp::Directive::OMPD_unknown,
8052 /*ForceSimpleCall=*/false,
8053 /*CheckCancelFlag=*/false);
8054 if (!AfterIP)
8055 return AfterIP.takeError();
8056 }
8057 return Builder.saveIP();
8058}
8059
8061 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8062 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8063
8064 if (!updateToLocation(Loc))
8065 return Loc.IP;
8066
8067 Directive OMPD = Directive::OMPD_critical;
8068 uint32_t SrcLocStrSize;
8069 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8070 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8071 Value *ThreadId = getOrCreateThreadID(Ident);
8072 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8073 Value *Args[] = {Ident, ThreadId, LockVar};
8074
8075 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8076 Function *RTFn = nullptr;
8077 if (HintInst) {
8078 // Add Hint to entry Args and create call
8079 EnterArgs.push_back(HintInst);
8080 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8081 } else {
8082 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8083 }
8084 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8085
8086 Function *ExitRTLFn =
8087 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8088 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8089
8090 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8091 /*Conditional*/ false, /*hasFinalize*/ true);
8092}
8093
8096 InsertPointTy AllocaIP, unsigned NumLoops,
8097 ArrayRef<llvm::Value *> StoreValues,
8098 const Twine &Name, bool IsDependSource) {
8099 assert(
8100 llvm::all_of(StoreValues,
8101 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8102 "OpenMP runtime requires depend vec with i64 type");
8103
8104 if (!updateToLocation(Loc))
8105 return Loc.IP;
8106
8107 // Allocate space for vector and generate alloc instruction.
8108 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8109 Builder.restoreIP(AllocaIP);
8110 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8111 ArgsBase->setAlignment(Align(8));
8113
8114 // Store the index value with offset in depend vector.
8115 for (unsigned I = 0; I < NumLoops; ++I) {
8116 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8117 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8118 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8119 STInst->setAlignment(Align(8));
8120 }
8121
8122 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8123 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8124
8125 uint32_t SrcLocStrSize;
8126 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8127 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8128 Value *ThreadId = getOrCreateThreadID(Ident);
8129 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8130
8131 Function *RTLFn = nullptr;
8132 if (IsDependSource)
8133 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8134 else
8135 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8136 createRuntimeFunctionCall(RTLFn, Args);
8137
8138 return Builder.saveIP();
8139}
8140
8142 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8143 FinalizeCallbackTy FiniCB, bool IsThreads) {
8144 if (!updateToLocation(Loc))
8145 return Loc.IP;
8146
8147 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8148 Instruction *EntryCall = nullptr;
8149 Instruction *ExitCall = nullptr;
8150
8151 if (IsThreads) {
8152 uint32_t SrcLocStrSize;
8153 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8154 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8155 Value *ThreadId = getOrCreateThreadID(Ident);
8156 Value *Args[] = {Ident, ThreadId};
8157
8158 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8159 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8160
8161 Function *ExitRTLFn =
8162 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8163 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8164 }
8165
8166 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8167 /*Conditional*/ false, /*hasFinalize*/ true);
8168}
8169
8170OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8171 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8172 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8173 bool HasFinalize, bool IsCancellable) {
8174
8175 if (HasFinalize)
8176 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8177
8178 // Create inlined region's entry and body blocks, in preparation
8179 // for conditional creation
8180 BasicBlock *EntryBB = Builder.GetInsertBlock();
8181 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8183 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8184 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8185 BasicBlock *FiniBB =
8186 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8187
8188 Builder.SetInsertPoint(EntryBB->getTerminator());
8189 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8190
8191 // generate body
8192 if (Error Err =
8193 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8194 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8195 return Err;
8196
8197 // emit exit call and do any needed finalization.
8198 auto FinIP = InsertPointTy(FiniBB, FiniBB->getFirstInsertionPt());
8199 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8200 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8201 "Unexpected control flow graph state!!");
8202 InsertPointOrErrorTy AfterIP =
8203 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8204 if (!AfterIP)
8205 return AfterIP.takeError();
8206
8207 // If we are skipping the region of a non conditional, remove the exit
8208 // block, and clear the builder's insertion point.
8209 assert(SplitPos->getParent() == ExitBB &&
8210 "Unexpected Insertion point location!");
8211 auto merged = MergeBlockIntoPredecessor(ExitBB);
8212 BasicBlock *ExitPredBB = SplitPos->getParent();
8213 auto InsertBB = merged ? ExitPredBB : ExitBB;
8215 SplitPos->eraseFromParent();
8216 Builder.SetInsertPoint(InsertBB);
8217
8218 return Builder.saveIP();
8219}
8220
8221OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8222 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8223 // if nothing to do, Return current insertion point.
8224 if (!Conditional || !EntryCall)
8225 return Builder.saveIP();
8226
8227 BasicBlock *EntryBB = Builder.GetInsertBlock();
8228 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8229 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8230 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8231
8232 // Emit thenBB and set the Builder's insertion point there for
8233 // body generation next. Place the block after the current block.
8234 Function *CurFn = EntryBB->getParent();
8235 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8236
8237 // Move Entry branch to end of ThenBB, and replace with conditional
8238 // branch (If-stmt)
8239 Instruction *EntryBBTI = EntryBB->getTerminator();
8240 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8241 EntryBBTI->removeFromParent();
8242 Builder.SetInsertPoint(UI);
8243 Builder.Insert(EntryBBTI);
8244 UI->eraseFromParent();
8245 Builder.SetInsertPoint(ThenBB->getTerminator());
8246
8247 // return an insertion point to ExitBB.
8248 return IRBuilder<>::InsertPoint(ExitBB, ExitBB->getFirstInsertionPt());
8249}
8250
8251OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8252 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8253 bool HasFinalize) {
8254
8255 Builder.restoreIP(FinIP);
8256
8257 // If there is finalization to do, emit it before the exit call
8258 if (HasFinalize) {
8259 assert(!FinalizationStack.empty() &&
8260 "Unexpected finalization stack state!");
8261
8262 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8263 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8264
8265 if (Error Err = Fi.mergeFiniBB(Builder, FinIP.getBlock()))
8266 return std::move(Err);
8267
8268 // Exit condition: insertion point is before the terminator of the new Fini
8269 // block
8270 Builder.SetInsertPoint(FinIP.getBlock()->getTerminator());
8271 }
8272
8273 if (!ExitCall)
8274 return Builder.saveIP();
8275
8276 // place the Exitcall as last instruction before Finalization block terminator
8277 ExitCall->removeFromParent();
8278 Builder.Insert(ExitCall);
8279
8280 return IRBuilder<>::InsertPoint(ExitCall->getParent(),
8281 ExitCall->getIterator());
8282}
8283
8285 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8286 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8287 if (!IP.isSet())
8288 return IP;
8289
8291
8292 // creates the following CFG structure
8293 // OMP_Entry : (MasterAddr != PrivateAddr)?
8294 // F T
8295 // | \
8296 // | copin.not.master
8297 // | /
8298 // v /
8299 // copyin.not.master.end
8300 // |
8301 // v
8302 // OMP.Entry.Next
8303
8304 BasicBlock *OMP_Entry = IP.getBlock();
8305 Function *CurFn = OMP_Entry->getParent();
8306 BasicBlock *CopyBegin =
8307 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8308 BasicBlock *CopyEnd = nullptr;
8309
8310 // If entry block is terminated, split to preserve the branch to following
8311 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8313 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8314 "copyin.not.master.end");
8315 OMP_Entry->getTerminator()->eraseFromParent();
8316 } else {
8317 CopyEnd =
8318 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8319 }
8320
8321 Builder.SetInsertPoint(OMP_Entry);
8322 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8323 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8324 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8325 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8326
8327 Builder.SetInsertPoint(CopyBegin);
8328 if (BranchtoEnd)
8329 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8330
8331 return Builder.saveIP();
8332}
8333
8335 Value *Size, Value *Allocator,
8336 std::string Name) {
8338 if (!updateToLocation(Loc))
8339 return nullptr;
8340
8341 uint32_t SrcLocStrSize;
8342 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8343 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8344 Value *ThreadId = getOrCreateThreadID(Ident);
8345 Value *Args[] = {ThreadId, Size, Allocator};
8346
8347 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8348
8349 return createRuntimeFunctionCall(Fn, Args, Name);
8350}
8351
8353 Value *Align, Value *Size,
8354 Value *Allocator,
8355 std::string Name) {
8357 if (!updateToLocation(Loc))
8358 return nullptr;
8359
8360 uint32_t SrcLocStrSize;
8361 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8362 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8363 Value *ThreadId = getOrCreateThreadID(Ident);
8364 Value *Args[] = {ThreadId, Align, Size, Allocator};
8365
8366 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8367
8368 return Builder.CreateCall(Fn, Args, Name);
8369}
8370
8372 Value *Addr, Value *Allocator,
8373 std::string Name) {
8375 if (!updateToLocation(Loc))
8376 return nullptr;
8377
8378 uint32_t SrcLocStrSize;
8379 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8380 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8381 Value *ThreadId = getOrCreateThreadID(Ident);
8382 Value *Args[] = {ThreadId, Addr, Allocator};
8383 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8384 return createRuntimeFunctionCall(Fn, Args, Name);
8385}
8386
8388 Value *Size,
8389 const Twine &Name) {
8392
8393 Value *Args[] = {Size};
8394 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8395 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8397 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8398 return Call;
8399}
8400
8402 Type *VarType,
8403 const Twine &Name) {
8404 return createOMPAllocShared(
8405 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8406}
8407
8409 Value *Addr, Value *Size,
8410 const Twine &Name) {
8413
8414 Value *Args[] = {Addr, Size};
8415 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8416 return Builder.CreateCall(Fn, Args, Name);
8417}
8418
8420 Value *Addr, Type *VarType,
8421 const Twine &Name) {
8422 return createOMPFreeShared(
8423 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8424 Name);
8425}
8426
8428 const LocationDescription &Loc, Value *InteropVar,
8430 Value *DependenceAddress, bool HaveNowaitClause) {
8433
8434 uint32_t SrcLocStrSize;
8435 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8436 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8437 Value *ThreadId = getOrCreateThreadID(Ident);
8438 if (Device == nullptr)
8440 else if (Device->getType() != Int32)
8441 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8442 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8443 if (NumDependences == nullptr) {
8444 NumDependences = ConstantInt::get(Int32, 0);
8445 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8446 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8447 }
8448 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8449 Value *Args[] = {
8450 Ident, ThreadId, InteropVar, InteropTypeVal,
8451 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8452
8453 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8454
8455 return createRuntimeFunctionCall(Fn, Args);
8456}
8457
8459 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8460 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8463
8464 uint32_t SrcLocStrSize;
8465 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8466 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8467 Value *ThreadId = getOrCreateThreadID(Ident);
8468 if (Device == nullptr)
8470 else if (Device->getType() != Int32)
8471 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8472 if (NumDependences == nullptr) {
8473 NumDependences = ConstantInt::get(Int32, 0);
8474 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8475 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8476 }
8477 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8478 Value *Args[] = {
8479 Ident, ThreadId, InteropVar, Device,
8480 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8481
8482 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8483
8484 return createRuntimeFunctionCall(Fn, Args);
8485}
8486
8488 Value *InteropVar, Value *Device,
8489 Value *NumDependences,
8490 Value *DependenceAddress,
8491 bool HaveNowaitClause) {
8494 uint32_t SrcLocStrSize;
8495 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8496 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8497 Value *ThreadId = getOrCreateThreadID(Ident);
8498 if (Device == nullptr)
8500 else if (Device->getType() != Int32)
8501 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8502 if (NumDependences == nullptr) {
8503 NumDependences = ConstantInt::get(Int32, 0);
8504 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8505 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8506 }
8507 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8508 Value *Args[] = {
8509 Ident, ThreadId, InteropVar, Device,
8510 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8511
8512 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8513
8514 return createRuntimeFunctionCall(Fn, Args);
8515}
8516
8519 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8522
8523 uint32_t SrcLocStrSize;
8524 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8525 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8526 Value *ThreadId = getOrCreateThreadID(Ident);
8527 Constant *ThreadPrivateCache =
8528 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8529 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8530
8531 Function *Fn =
8532 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8533
8534 return createRuntimeFunctionCall(Fn, Args);
8535}
8536
8538 const LocationDescription &Loc,
8540 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8541 "expected num_threads and num_teams to be specified");
8542
8543 if (!updateToLocation(Loc))
8544 return Loc.IP;
8545
8546 uint32_t SrcLocStrSize;
8547 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8548 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8549 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8550 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8551 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8552 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8553 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8554 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8555
8556 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8557 Function *Kernel = DebugKernelWrapper;
8558
8559 // We need to strip the debug prefix to get the correct kernel name.
8560 StringRef KernelName = Kernel->getName();
8561 const std::string DebugPrefix = "_debug__";
8562 if (KernelName.ends_with(DebugPrefix)) {
8563 KernelName = KernelName.drop_back(DebugPrefix.length());
8564 Kernel = M.getFunction(KernelName);
8565 assert(Kernel && "Expected the real kernel to exist");
8566 }
8567
8568 // Manifest the launch configuration in the metadata matching the kernel
8569 // environment.
8570 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8571 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8572 Attrs.MaxTeams.front());
8573
8574 // If MaxThreads is not set and needs adjustment, select the maximum between
8575 // the default workgroup size and the MinThreads value.
8576 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8577 if (MaxThreadsVal < 0 && UseDefaultMaxThreads) {
8578 if (hasGridValue(T)) {
8579 MaxThreadsVal =
8580 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8581 Attrs.MinThreads.front());
8582 } else {
8583 MaxThreadsVal = Attrs.MinThreads.front();
8584 }
8585 }
8586
8587 if (MaxThreadsVal > 0)
8588 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8589 MaxThreadsVal);
8590
8591 Constant *MinThreads =
8592 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8593 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8594 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8595 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8596 Constant *ReductionDataSize =
8597 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8598
8600 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8601 const DataLayout &DL = Fn->getDataLayout();
8602
8603 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8604 Constant *DynamicEnvironmentInitializer =
8605 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8606 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8607 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8608 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8609 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8610 DL.getDefaultGlobalsAddressSpace());
8611 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8612
8613 Constant *DynamicEnvironment =
8614 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8615 ? DynamicEnvironmentGV
8616 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8617 DynamicEnvironmentPtr);
8618
8619 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8620 ConfigurationEnvironment, {
8621 UseGenericStateMachineVal,
8622 MayUseNestedParallelismVal,
8623 IsSPMDVal,
8624 MinThreads,
8625 MaxThreads,
8626 MinTeams,
8627 MaxTeams,
8628 ReductionDataSize,
8629 });
8630 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8631 KernelEnvironment, {
8632 ConfigurationEnvironmentInitializer,
8633 Ident,
8634 DynamicEnvironment,
8635 });
8636 std::string KernelEnvironmentName =
8637 (KernelName + "_kernel_environment").str();
8638 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8639 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8640 KernelEnvironmentInitializer, KernelEnvironmentName,
8641 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8642 DL.getDefaultGlobalsAddressSpace());
8643 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8644
8645 Constant *KernelEnvironment =
8646 KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8647 ? KernelEnvironmentGV
8648 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8649 KernelEnvironmentPtr);
8650 Value *KernelLaunchEnvironment =
8651 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8652 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8653 KernelLaunchEnvironment =
8654 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8655 ? KernelLaunchEnvironment
8656 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8657 KernelLaunchEnvParamTy);
8658 CallInst *ThreadKind = createRuntimeFunctionCall(
8659 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8660
8661 Value *ExecUserCode = Builder.CreateICmpEQ(
8662 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8663 "exec_user_code");
8664
8665 // ThreadKind = __kmpc_target_init(...)
8666 // if (ThreadKind == -1)
8667 // user_code
8668 // else
8669 // return;
8670
8671 auto *UI = Builder.CreateUnreachable();
8672 BasicBlock *CheckBB = UI->getParent();
8673 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8674
8675 BasicBlock *WorkerExitBB = BasicBlock::Create(
8676 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8677 Builder.SetInsertPoint(WorkerExitBB);
8678 Builder.CreateRetVoid();
8679
8680 auto *CheckBBTI = CheckBB->getTerminator();
8681 Builder.SetInsertPoint(CheckBBTI);
8682 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8683
8684 CheckBBTI->eraseFromParent();
8685 UI->eraseFromParent();
8686
8687 // Continue in the "user_code" block, see diagram above and in
8688 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8689 return InsertPointTy(UserCodeEntryBB, UserCodeEntryBB->getFirstInsertionPt());
8690}
8691
8693 int32_t TeamsReductionDataSize) {
8694 if (!updateToLocation(Loc))
8695 return;
8696
8698 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8699
8701
8702 if (!TeamsReductionDataSize)
8703 return;
8704
8705 Function *Kernel = Builder.GetInsertBlock()->getParent();
8706 // We need to strip the debug prefix to get the correct kernel name.
8707 StringRef KernelName = Kernel->getName();
8708 const std::string DebugPrefix = "_debug__";
8709 if (KernelName.ends_with(DebugPrefix))
8710 KernelName = KernelName.drop_back(DebugPrefix.length());
8711 auto *KernelEnvironmentGV =
8712 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8713 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8714 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8715 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8716 KernelEnvironmentInitializer,
8717 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8718 KernelEnvironmentGV->setInitializer(NewInitializer);
8719}
8720
8721static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8722 bool Min) {
8723 if (Kernel.hasFnAttribute(Name)) {
8724 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8725 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8726 }
8727 Kernel.addFnAttr(Name, llvm::utostr(Value));
8728}
8729
8730std::pair<int32_t, int32_t>
8732 int32_t ThreadLimit =
8733 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8734
8735 if (T.isAMDGPU()) {
8736 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8737 if (!Attr.isValid() || !Attr.isStringAttribute())
8738 return {0, ThreadLimit};
8739 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8740 int32_t LB, UB;
8741 if (!llvm::to_integer(UBStr, UB, 10))
8742 return {0, ThreadLimit};
8743 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8744 if (!llvm::to_integer(LBStr, LB, 10))
8745 return {0, UB};
8746 return {LB, UB};
8747 }
8748
8749 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8750 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8751 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8752 }
8753 return {0, ThreadLimit};
8754}
8755
8757 Function &Kernel, int32_t LB,
8758 int32_t UB) {
8759 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8760
8761 if (T.isAMDGPU()) {
8762 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8763 llvm::utostr(LB) + "," + llvm::utostr(UB));
8764 return;
8765 }
8766
8768}
8769
8770std::pair<int32_t, int32_t>
8772 // TODO: Read from backend annotations if available.
8773 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8774}
8775
8777 int32_t LB, int32_t UB) {
8778 if (UB > 0) {
8779 if (T.isNVPTX())
8781 if (T.isAMDGPU())
8782 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8783 }
8784
8785 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8786}
8787
8788void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8789 Function *OutlinedFn) {
8790 if (Config.isTargetDevice()) {
8792 // TODO: Determine if DSO local can be set to true.
8793 OutlinedFn->setDSOLocal(false);
8795 if (T.isAMDGCN())
8797 else if (T.isNVPTX())
8799 else if (T.isSPIRV())
8801 }
8802}
8803
8804Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8805 StringRef EntryFnIDName) {
8806 if (Config.isTargetDevice()) {
8807 assert(OutlinedFn && "The outlined function must exist if embedded");
8808 return OutlinedFn;
8809 }
8810
8811 return new GlobalVariable(
8812 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8813 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8814}
8815
8816Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8817 StringRef EntryFnName) {
8818 if (OutlinedFn)
8819 return OutlinedFn;
8820
8821 assert(!M.getGlobalVariable(EntryFnName, true) &&
8822 "Named kernel already exists?");
8823 return new GlobalVariable(
8824 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8825 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8826}
8827
8829 TargetRegionEntryInfo &EntryInfo,
8830 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8831 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8832
8833 SmallString<64> EntryFnName;
8834 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8835
8836 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8837 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8838 if (!CBResult)
8839 return CBResult.takeError();
8840 OutlinedFn = *CBResult;
8841 } else {
8842 OutlinedFn = nullptr;
8843 }
8844
8845 // If this target outline function is not an offload entry, we don't need to
8846 // register it. This may be in the case of a false if clause, or if there are
8847 // no OpenMP targets.
8848 if (!IsOffloadEntry)
8849 return Error::success();
8850
8851 std::string EntryFnIDName =
8852 Config.isTargetDevice()
8853 ? std::string(EntryFnName)
8854 : createPlatformSpecificName({EntryFnName, "region_id"});
8855
8856 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8857 EntryFnName, EntryFnIDName);
8858 return Error::success();
8859}
8860
8862 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8863 StringRef EntryFnName, StringRef EntryFnIDName) {
8864 if (OutlinedFn)
8865 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8866 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8867 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8868 OffloadInfoManager.registerTargetRegionEntryInfo(
8869 EntryInfo, EntryAddr, OutlinedFnID,
8871 return OutlinedFnID;
8872}
8873
8875 const LocationDescription &Loc, InsertPointTy AllocaIP,
8876 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8877 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8878 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8879 omp::RuntimeFunction *MapperFunc,
8881 BodyGenTy BodyGenType)>
8882 BodyGenCB,
8883 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8884 if (!updateToLocation(Loc))
8885 return InsertPointTy();
8886
8887 Builder.restoreIP(CodeGenIP);
8888
8889 bool IsStandAlone = !BodyGenCB;
8890 MapInfosTy *MapInfo;
8891 // Generate the code for the opening of the data environment. Capture all the
8892 // arguments of the runtime call by reference because they are used in the
8893 // closing of the region.
8894 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8895 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8896 MapInfo = &GenMapInfoCB(Builder.saveIP());
8897 if (Error Err = emitOffloadingArrays(
8898 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
8899 /*IsNonContiguous=*/true, DeviceAddrCB))
8900 return Err;
8901
8902 TargetDataRTArgs RTArgs;
8904
8905 // Emit the number of elements in the offloading arrays.
8906 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8907
8908 // Source location for the ident struct
8909 if (!SrcLocInfo) {
8910 uint32_t SrcLocStrSize;
8911 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8912 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8913 }
8914
8915 SmallVector<llvm::Value *, 13> OffloadingArgs = {
8916 SrcLocInfo, DeviceID,
8917 PointerNum, RTArgs.BasePointersArray,
8918 RTArgs.PointersArray, RTArgs.SizesArray,
8919 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8920 RTArgs.MappersArray};
8921
8922 if (IsStandAlone) {
8923 assert(MapperFunc && "MapperFunc missing for standalone target data");
8924
8925 auto TaskBodyCB = [&](Value *, Value *,
8927 if (Info.HasNoWait) {
8928 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
8932 }
8933
8935 OffloadingArgs);
8936
8937 if (Info.HasNoWait) {
8938 BasicBlock *OffloadContBlock =
8939 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
8940 Function *CurFn = Builder.GetInsertBlock()->getParent();
8941 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
8942 Builder.restoreIP(Builder.saveIP());
8943 }
8944 return Error::success();
8945 };
8946
8947 bool RequiresOuterTargetTask = Info.HasNoWait;
8948 if (!RequiresOuterTargetTask)
8949 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
8950 /*TargetTaskAllocaIP=*/{}));
8951 else
8952 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
8953 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
8954 } else {
8955 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
8956 omp::OMPRTL___tgt_target_data_begin_mapper);
8957
8958 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
8959
8960 for (auto DeviceMap : Info.DevicePtrInfoMap) {
8961 if (isa<AllocaInst>(DeviceMap.second.second)) {
8962 auto *LI =
8963 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
8964 Builder.CreateStore(LI, DeviceMap.second.second);
8965 }
8966 }
8967
8968 // If device pointer privatization is required, emit the body of the
8969 // region here. It will have to be duplicated: with and without
8970 // privatization.
8971 InsertPointOrErrorTy AfterIP =
8972 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
8973 if (!AfterIP)
8974 return AfterIP.takeError();
8975 Builder.restoreIP(*AfterIP);
8976 }
8977 return Error::success();
8978 };
8979
8980 // If we need device pointer privatization, we need to emit the body of the
8981 // region with no privatization in the 'else' branch of the conditional.
8982 // Otherwise, we don't have to do anything.
8983 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8984 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8985 InsertPointOrErrorTy AfterIP =
8986 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
8987 if (!AfterIP)
8988 return AfterIP.takeError();
8989 Builder.restoreIP(*AfterIP);
8990 return Error::success();
8991 };
8992
8993 // Generate code for the closing of the data region.
8994 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8995 ArrayRef<BasicBlock *> DeallocBlocks) {
8996 TargetDataRTArgs RTArgs;
8997 Info.EmitDebug = !MapInfo->Names.empty();
8998 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
8999
9000 // Emit the number of elements in the offloading arrays.
9001 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9002
9003 // Source location for the ident struct
9004 if (!SrcLocInfo) {
9005 uint32_t SrcLocStrSize;
9006 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9007 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9008 }
9009
9010 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9011 PointerNum, RTArgs.BasePointersArray,
9012 RTArgs.PointersArray, RTArgs.SizesArray,
9013 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9014 RTArgs.MappersArray};
9015 Function *EndMapperFunc =
9016 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9017
9018 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9019 return Error::success();
9020 };
9021
9022 // We don't have to do anything to close the region if the if clause evaluates
9023 // to false.
9024 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9025 ArrayRef<BasicBlock *> DeallocBlocks) {
9026 return Error::success();
9027 };
9028
9029 Error Err = [&]() -> Error {
9030 if (BodyGenCB) {
9031 Error Err = [&]() {
9032 if (IfCond)
9033 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9034 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9035 }();
9036
9037 if (Err)
9038 return Err;
9039
9040 // If we don't require privatization of device pointers, we emit the body
9041 // in between the runtime calls. This avoids duplicating the body code.
9042 InsertPointOrErrorTy AfterIP =
9043 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9044 if (!AfterIP)
9045 return AfterIP.takeError();
9046 restoreIPandDebugLoc(Builder, *AfterIP);
9047
9048 if (IfCond)
9049 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9050 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9051 }
9052 if (IfCond)
9053 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9054 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9055 }();
9056
9057 if (Err)
9058 return Err;
9059
9060 return Builder.saveIP();
9061}
9062
9065 bool IsGPUDistribute) {
9066 assert((IVSize == 32 || IVSize == 64) &&
9067 "IV size is not compatible with the omp runtime");
9068 RuntimeFunction Name;
9069 if (IsGPUDistribute)
9070 Name = IVSize == 32
9071 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9072 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9073 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9074 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9075 else
9076 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9077 : omp::OMPRTL___kmpc_for_static_init_4u)
9078 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9079 : omp::OMPRTL___kmpc_for_static_init_8u);
9080
9081 return getOrCreateRuntimeFunction(M, Name);
9082}
9083
9085 bool IVSigned) {
9086 assert((IVSize == 32 || IVSize == 64) &&
9087 "IV size is not compatible with the omp runtime");
9088 RuntimeFunction Name = IVSize == 32
9089 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9090 : omp::OMPRTL___kmpc_dispatch_init_4u)
9091 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9092 : omp::OMPRTL___kmpc_dispatch_init_8u);
9093
9094 return getOrCreateRuntimeFunction(M, Name);
9095}
9096
9098 bool IVSigned) {
9099 assert((IVSize == 32 || IVSize == 64) &&
9100 "IV size is not compatible with the omp runtime");
9101 RuntimeFunction Name = IVSize == 32
9102 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9103 : omp::OMPRTL___kmpc_dispatch_next_4u)
9104 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9105 : omp::OMPRTL___kmpc_dispatch_next_8u);
9106
9107 return getOrCreateRuntimeFunction(M, Name);
9108}
9109
9111 bool IVSigned) {
9112 assert((IVSize == 32 || IVSize == 64) &&
9113 "IV size is not compatible with the omp runtime");
9114 RuntimeFunction Name = IVSize == 32
9115 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9116 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9117 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9118 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9119
9120 return getOrCreateRuntimeFunction(M, Name);
9121}
9122
9124 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9125}
9126
9128 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9129 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9130
9131 DISubprogram *NewSP = Func->getSubprogram();
9132 if (!NewSP)
9133 return;
9134
9136
9137 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9138 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9139 // Only use cached variable if the arg number matches. This is important
9140 // so that DIVariable created for privatized variables are not discarded.
9141 if (NewVar && (arg == NewVar->getArg()))
9142 return NewVar;
9143
9145 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9146 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9147 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9148 return NewVar;
9149 };
9150
9151 auto UpdateDebugRecord = [&](auto *DR) {
9152 DILocalVariable *OldVar = DR->getVariable();
9153 unsigned ArgNo = 0;
9154 for (auto Loc : DR->location_ops()) {
9155 auto Iter = ValueReplacementMap.find(Loc);
9156 if (Iter != ValueReplacementMap.end()) {
9157 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9158 ArgNo = std::get<1>(Iter->second) + 1;
9159 }
9160 }
9161 if (ArgNo != 0)
9162 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9163 };
9164
9166 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9167 if (DVR->getNumVariableLocationOps() != 1u) {
9168 DVR->setKillLocation();
9169 return;
9170 }
9171 Value *Loc = DVR->getVariableLocationOp(0u);
9172 BasicBlock *CurBB = DVR->getParent();
9173 BasicBlock *RequiredBB = nullptr;
9174
9175 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9176 RequiredBB = LocInst->getParent();
9177 else if (isa<llvm::Argument>(Loc))
9178 RequiredBB = &DVR->getFunction()->getEntryBlock();
9179
9180 if (RequiredBB && RequiredBB != CurBB) {
9181 assert(!RequiredBB->empty());
9182 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9183 RequiredBB->back().getIterator());
9184 DVRsToDelete.push_back(DVR);
9185 }
9186 };
9187
9188 // The location and scope of variable intrinsics and records still point to
9189 // the parent function of the target region. Update them.
9190 for (Instruction &I : instructions(Func)) {
9192 "Unexpected debug intrinsic");
9193 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9194 UpdateDebugRecord(&DVR);
9195 MoveDebugRecordToCorrectBlock(&DVR);
9196 }
9197 }
9198 for (auto *DVR : DVRsToDelete)
9199 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9200 // An extra argument is passed to the device. Create the debug data for it.
9201 if (OMPBuilder.Config.isTargetDevice()) {
9202 DICompileUnit *CU = NewSP->getUnit();
9203 Module *M = Func->getParent();
9204 DIBuilder DB(*M, true, CU);
9205 DIType *VoidPtrTy =
9206 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9207 unsigned ArgNo = Func->arg_size();
9208 DILocalVariable *Var = DB.createParameterVariable(
9209 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9210 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9211 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9212 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9213 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9214 &(*Func->begin()));
9215 }
9216}
9217
9219 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9220 return cast<Operator>(V)->getOperand(0);
9221 return V;
9222}
9223
9225 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9227 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9230 DebugLoc OutlinedFnLoc) {
9231 SmallVector<Type *> ParameterTypes;
9232 if (OMPBuilder.Config.isTargetDevice()) {
9233 // All parameters to target devices are passed as pointers
9234 // or i64. This assumes 64-bit address spaces/pointers.
9235 for (auto &Arg : Inputs)
9236 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9237 ? Arg->getType()
9238 : Type::getInt64Ty(Builder.getContext()));
9239 } else {
9240 for (auto &Arg : Inputs)
9241 ParameterTypes.push_back(Arg->getType());
9242 }
9243
9244 // The implicit dyn_ptr argument is always the last parameter on both host
9245 // and device so the argument counts match without runtime manipulation.
9246 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9247 ParameterTypes.push_back(PtrTy);
9248
9249 auto BB = Builder.GetInsertBlock();
9250 auto M = BB->getModule();
9251 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9252 /*isVarArg*/ false);
9253 auto Func =
9254 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9255
9256 // Forward target-cpu and target-features function attributes from the
9257 // original function to the new outlined function.
9258 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9259
9260 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9261 if (TargetCpuAttr.isStringAttribute())
9262 Func->addFnAttr(TargetCpuAttr);
9263
9264 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9265 if (TargetFeaturesAttr.isStringAttribute())
9266 Func->addFnAttr(TargetFeaturesAttr);
9267
9268 if (OMPBuilder.Config.isTargetDevice()) {
9269 Value *ExecMode =
9270 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9271 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9272 }
9273
9274 // Save insert point.
9275 IRBuilder<>::InsertPointGuard IPG(Builder);
9276 // We will generate the entries in the outlined function but the debug
9277 // location is still pointing to the parent function, which is the wrong
9278 // scope. OutlinedFnLoc, when the caller provides one, is the same source
9279 // position scoped to the subprogram that will be attached to the outlined
9280 // function, so it is what everything emitted below needs.
9281 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9282
9283 // Generate the region into the function.
9284 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9285 Builder.SetInsertPoint(EntryBB);
9286
9287 // Insert target init call in the device compilation pass.
9288 if (OMPBuilder.Config.isTargetDevice())
9289 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9290
9291 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9292
9293 // As we embed the user code in the middle of our target region after we
9294 // generate entry code, we must move what allocas we can into the entry
9295 // block to avoid possible breaking optimisations for device
9296 if (OMPBuilder.Config.isTargetDevice())
9298
9299 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9300 BasicBlock *OutlinedBodyBB =
9301 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9303 Builder.saveIP(),
9304 OpenMPIRBuilder::InsertPointTy(OutlinedBodyBB, OutlinedBodyBB->begin()),
9305 ExitBB);
9306 if (!AfterIP)
9307 return AfterIP.takeError();
9308 Builder.SetInsertPoint(ExitBB);
9309
9310 // Insert target deinit call in the device compilation pass.
9311 if (OMPBuilder.Config.isTargetDevice())
9312 OMPBuilder.createTargetDeinit(Builder);
9313
9314 // Insert return instruction.
9315 Builder.CreateRetVoid();
9316
9317 // New Alloca IP at entry point of created device function.
9318 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9319 auto AllocaIP = Builder.saveIP();
9320
9321 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());
9322
9323 // Do not include the artificial dyn_ptr argument.
9324 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9325
9327
9328 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9329 // Things like GEP's can come in the form of Constants. Constants and
9330 // ConstantExpr's do not have access to the knowledge of what they're
9331 // contained in, so we must dig a little to find an instruction so we
9332 // can tell if they're used inside of the function we're outlining. We
9333 // also replace the original constant expression with a new instruction
9334 // equivalent; an instruction as it allows easy modification in the
9335 // following loop, as we can now know the constant (instruction) is
9336 // owned by our target function and replaceUsesOfWith can now be invoked
9337 // on it (cannot do this with constants it seems). A brand new one also
9338 // allows us to be cautious as it is perhaps possible the old expression
9339 // was used inside of the function but exists and is used externally
9340 // (unlikely by the nature of a Constant, but still).
9341 // NOTE: We cannot remove dead constants that have been rewritten to
9342 // instructions at this stage, we run the risk of breaking later lowering
9343 // by doing so as we could still be in the process of lowering the module
9344 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9345 // constants we have created rewritten versions of.
9346 if (auto *Const = dyn_cast<Constant>(Input))
9347 convertUsersOfConstantsToInstructions(Const, Func, false);
9348
9349 // Collect users before iterating over them to avoid invalidating the
9350 // iteration in case a user uses Input more than once (e.g. a call
9351 // instruction).
9352 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9353 // Collect all the instructions
9355 if (auto *Instr = dyn_cast<Instruction>(User))
9356 if (Instr->getFunction() == Func)
9357 Instr->replaceUsesOfWith(Input, InputCopy);
9358 };
9359
9360 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9361
9362 // Rewrite uses of input valus to parameters.
9363 for (auto InArg : zip(Inputs, ArgRange)) {
9364 Value *Input = std::get<0>(InArg);
9365 Argument &Arg = std::get<1>(InArg);
9366 Value *InputCopy = nullptr;
9367
9368 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9369 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(),
9370 OpenMPIRBuilder::InsertPointTy(ExitBB, ExitBB->begin()));
9371 if (!AfterIP)
9372 return AfterIP.takeError();
9373 Builder.restoreIP(*AfterIP);
9374 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9375
9376 // In certain cases a Global may be set up for replacement, however, this
9377 // Global may be used in multiple arguments to the kernel, just segmented
9378 // apart, for example, if we have a global array, that is sectioned into
9379 // multiple mappings (technically not legal in OpenMP, but there is a case
9380 // in Fortran for Common Blocks where this is neccesary), we will end up
9381 // with GEP's into this array inside the kernel, that refer to the Global
9382 // but are technically separate arguments to the kernel for all intents and
9383 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9384 // index, it will fold into an referal to the Global, if we then encounter
9385 // this folded GEP during replacement all of the references to the
9386 // Global in the kernel will be replaced with the argument we have generated
9387 // that corresponds to it, including any other GEP's that refer to the
9388 // Global that may be other arguments. This will invalidate all of the other
9389 // preceding mapped arguments that refer to the same global that may be
9390 // separate segments. To prevent this, we defer global processing until all
9391 // other processing has been performed.
9394 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9395 continue;
9396 }
9397
9399 continue;
9400
9401 ReplaceValue(Input, InputCopy, Func);
9402 }
9403
9404 // Replace all of our deferred Input values, currently just Globals.
9405 for (auto Deferred : DeferredReplacement)
9406 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9407
9408 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9409 ValueReplacementMap);
9410 return Func;
9411}
9412/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9413/// of pointers containing shared data between the parent task and the created
9414/// task.
9416 IRBuilderBase &Builder,
9417 Value *TaskWithPrivates,
9418 Type *TaskWithPrivatesTy) {
9419
9420 Type *TaskTy = OMPIRBuilder.Task;
9421 LLVMContext &Ctx = Builder.getContext();
9422 Value *TaskT =
9423 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9424 Value *Shareds = TaskT;
9425 // TaskWithPrivatesTy can be one of the following
9426 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9427 // %struct.privates }
9428 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9429 //
9430 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9431 // its first member has to be the task descriptor. TaskTy is the type of the
9432 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9433 // first member of TaskT, gives us the pointer to shared data.
9434 if (TaskWithPrivatesTy != TaskTy)
9435 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9436 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9437}
9438/// Create an entry point for a target task with the following.
9439/// It'll have the following signature
9440/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9441/// This function is called from emitTargetTask once the
9442/// code to launch the target kernel has been outlined already.
9443/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9444/// into the task structure so that the deferred target task can access this
9445/// data even after the stack frame of the generating task has been rolled
9446/// back. Offloading arrays contain base pointers, pointers, sizes etc
9447/// of the data that the target kernel will access. These in effect are the
9448/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9450 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9451 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9452 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9453
9454 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9455 // This is because PrivatesTy is the type of the structure in which
9456 // we pass the offloading arrays to the deferred target task.
9457 assert((!NumOffloadingArrays || PrivatesTy) &&
9458 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9459 "to privatize");
9460
9461 Module &M = OMPBuilder.M;
9462 // KernelLaunchFunction is the target launch function, i.e.
9463 // the function that sets up kernel arguments and calls
9464 // __tgt_target_kernel to launch the kernel on the device.
9465 //
9466 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9467
9468 // StaleCI is the CallInst which is the call to the outlined
9469 // target kernel launch function. If there are local live-in values
9470 // that the outlined function uses then these are aggregated into a structure
9471 // which is passed as the second argument. If there are no local live-in
9472 // values or if all values used by the outlined kernel are global variables,
9473 // then there's only one argument, the threadID. So, StaleCI can be
9474 //
9475 // %structArg = alloca { ptr, ptr }, align 8
9476 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9477 // store ptr %20, ptr %gep_, align 8
9478 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9479 // store ptr %21, ptr %gep_8, align 8
9480 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9481 //
9482 // OR
9483 //
9484 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9486 StaleCI->getIterator());
9487
9488 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9489
9490 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9491 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9492 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9493
9494 auto ProxyFnTy =
9495 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9496 /* isVarArg */ false);
9497 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9498 ".omp_target_task_proxy_func", M);
9499 Value *ThreadId = ProxyFn->getArg(0);
9500 Value *TaskWithPrivates = ProxyFn->getArg(1);
9501 ThreadId->setName("thread.id");
9502 TaskWithPrivates->setName("task");
9503
9504 bool HasShareds = SharedArgsOperandNo > 0;
9505 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9506 IRBuilder<>::InsertPointGuard IPG(Builder);
9507 BasicBlock *EntryBB =
9508 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9509 Builder.SetInsertPoint(EntryBB);
9510 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9511
9512 SmallVector<Value *> KernelLaunchArgs;
9513 KernelLaunchArgs.reserve(StaleCI->arg_size());
9514 KernelLaunchArgs.push_back(ThreadId);
9515
9516 if (HasOffloadingArrays) {
9517 assert(TaskTy != TaskWithPrivatesTy &&
9518 "If there are offloading arrays to pass to the target"
9519 "TaskTy cannot be the same as TaskWithPrivatesTy");
9520 (void)TaskTy;
9521 Value *Privates =
9522 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9523 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9524 KernelLaunchArgs.push_back(
9525 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9526 }
9527
9528 if (HasShareds) {
9529 auto *ArgStructAlloca =
9530 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9531 assert(ArgStructAlloca &&
9532 "Unable to find the alloca instruction corresponding to arguments "
9533 "for extracted function");
9534 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9535 std::optional<TypeSize> ArgAllocSize =
9536 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9537 assert(ArgStructType && ArgAllocSize &&
9538 "Unable to determine size of arguments for extracted function");
9539 uint64_t StructSize = ArgAllocSize->getFixedValue();
9540
9541 AllocaInst *NewArgStructAlloca =
9542 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9543
9544 Value *SharedsSize = Builder.getInt64(StructSize);
9545
9547 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9548
9549 Builder.CreateMemCpy(
9550 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9551 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9552 KernelLaunchArgs.push_back(NewArgStructAlloca);
9553 }
9554 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9555 Builder.CreateRetVoid();
9556 return ProxyFn;
9557}
9559
9560 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9561 return GEP->getSourceElementType();
9562 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9563 return Alloca->getAllocatedType();
9564
9565 llvm_unreachable("Unhandled Instruction type");
9566 return nullptr;
9567}
9568// This function returns a struct that has at most two members.
9569// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9570// descriptor. The second member, if needed, is a struct containing arrays
9571// that need to be passed to the offloaded target kernel. For example,
9572// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9573// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9574// respectively, then the types created by this function are
9575//
9576// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9577// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9578// %struct.privates }
9579// %struct.task_with_privates is returned by this function.
9580// If there aren't any offloading arrays to pass to the target kernel,
9581// %struct.kmp_task_ompbuilder_t is returned.
9582static StructType *
9584 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9585
9586 if (OffloadingArraysToPrivatize.empty())
9587 return OMPIRBuilder.Task;
9588
9589 SmallVector<Type *, 4> StructFieldTypes;
9590 for (Value *V : OffloadingArraysToPrivatize) {
9591 assert(V->getType()->isPointerTy() &&
9592 "Expected pointer to array to privatize. Got a non-pointer value "
9593 "instead");
9594 Type *ArrayTy = getOffloadingArrayType(V);
9595 assert(ArrayTy && "ArrayType cannot be nullptr");
9596 StructFieldTypes.push_back(ArrayTy);
9597 }
9598 StructType *PrivatesStructTy =
9599 StructType::create(StructFieldTypes, "struct.privates");
9600 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9601 "struct.task_with_privates");
9602}
9604 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9605 TargetRegionEntryInfo &EntryInfo,
9607 Function *&OutlinedFn, Constant *&OutlinedFnID,
9611 DebugLoc OutlinedFnLoc) {
9612
9613 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9614 [&](StringRef EntryFnName) {
9615 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9616 EntryFnName, Inputs, CBFunc,
9617 ArgAccessorFuncCB, OutlinedFnLoc);
9618 };
9619
9620 return OMPBuilder.emitTargetRegionFunction(
9621 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9622 OutlinedFnID);
9623}
9624
9626 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9628 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9629 bool HasNoWait) {
9630
9631 // The following explains the code-gen scenario for the `target` directive. A
9632 // similar scneario is followed for other device-related directives (e.g.
9633 // `target enter data`) but in similar fashion since we only need to emit task
9634 // that encapsulates the proper runtime call.
9635 //
9636 // When we arrive at this function, the target region itself has been
9637 // outlined into the function OutlinedFn.
9638 // So at ths point, for
9639 // --------------------------------------------------------------
9640 // void user_code_that_offloads(...) {
9641 // omp target depend(..) map(from:a) map(to:b) private(i)
9642 // do i = 1, 10
9643 // a(i) = b(i) + n
9644 // }
9645 //
9646 // --------------------------------------------------------------
9647 //
9648 // we have
9649 //
9650 // --------------------------------------------------------------
9651 //
9652 // void user_code_that_offloads(...) {
9653 // %.offload_baseptrs = alloca [2 x ptr], align 8
9654 // %.offload_ptrs = alloca [2 x ptr], align 8
9655 // %.offload_mappers = alloca [2 x ptr], align 8
9656 // ;; target region has been outlined and now we need to
9657 // ;; offload to it via a target task.
9658 // }
9659 // void outlined_device_function(ptr a, ptr b, ptr n) {
9660 // n = *n_ptr;
9661 // do i = 1, 10
9662 // a(i) = b(i) + n
9663 // }
9664 //
9665 // We have to now do the following
9666 // (i) Make an offloading call to outlined_device_function using the OpenMP
9667 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9668 // emitted by emitKernelLaunch
9669 // (ii) Create a task entry point function that calls kernel_launch_function
9670 // and is the entry point for the target task. See
9671 // '@.omp_target_task_proxy_func in the pseudocode below.
9672 // (iii) Create a task with the task entry point created in (ii)
9673 //
9674 // That is we create the following
9675 // struct task_with_privates {
9676 // struct kmp_task_ompbuilder_t task_struct;
9677 // struct privates {
9678 // [2 x ptr] ; baseptrs
9679 // [2 x ptr] ; ptrs
9680 // [2 x i64] ; sizes
9681 // }
9682 // }
9683 // void user_code_that_offloads(...) {
9684 // %.offload_baseptrs = alloca [2 x ptr], align 8
9685 // %.offload_ptrs = alloca [2 x ptr], align 8
9686 // %.offload_sizes = alloca [2 x i64], align 8
9687 //
9688 // %structArg = alloca { ptr, ptr, ptr }, align 8
9689 // %strucArg[0] = a
9690 // %strucArg[1] = b
9691 // %strucArg[2] = &n
9692 //
9693 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9694 // sizeof(kmp_task_ompbuilder_t),
9695 // sizeof(structArg),
9696 // @.omp_target_task_proxy_func,
9697 // ...)
9698 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9699 // sizeof(structArg))
9700 // memcpy(target_task_with_privates->privates->baseptrs,
9701 // offload_baseptrs, sizeof(offload_baseptrs)
9702 // memcpy(target_task_with_privates->privates->ptrs,
9703 // offload_ptrs, sizeof(offload_ptrs)
9704 // memcpy(target_task_with_privates->privates->sizes,
9705 // offload_sizes, sizeof(offload_sizes)
9706 // dependencies_array = ...
9707 // ;; if nowait not present
9708 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9709 // call @__kmpc_omp_task_begin_if0(...)
9710 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9711 // %target_task_with_privates)
9712 // call @__kmpc_omp_task_complete_if0(...)
9713 // }
9714 //
9715 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9716 // ptr %task) {
9717 // %structArg = alloca {ptr, ptr, ptr}
9718 // %task_ptr = getelementptr(%task, 0, 0)
9719 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9720 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9721 //
9722 // %offloading_arrays = getelementptr(%task, 0, 1)
9723 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9724 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9725 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9726 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9727 // %offload_sizes, %structArg)
9728 // }
9729 //
9730 // We need the proxy function because the signature of the task entry point
9731 // expected by kmpc_omp_task is always the same and will be different from
9732 // that of the kernel_launch function.
9733 //
9734 // kernel_launch_function is generated by emitKernelLaunch and has the
9735 // always_inline attribute. For this example, it'll look like so:
9736 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9737 // %offload_sizes, %structArg) alwaysinline {
9738 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9739 // ; load aggregated data from %structArg
9740 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9741 // ; offload_sizes
9742 // call i32 @__tgt_target_kernel(...,
9743 // outlined_device_function,
9744 // ptr %kernel_args)
9745 // }
9746 // void outlined_device_function(ptr a, ptr b, ptr n) {
9747 // n = *n_ptr;
9748 // do i = 1, 10
9749 // a(i) = b(i) + n
9750 // }
9751 //
9752 BasicBlock *TargetTaskBodyBB =
9753 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9754 BasicBlock *TargetTaskAllocaBB =
9755 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9756
9757 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB,
9758 TargetTaskAllocaBB->begin());
9759 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB, TargetTaskBodyBB->begin());
9760
9761 auto OI = std::make_unique<OutlineInfo>();
9762 OI->EntryBB = TargetTaskAllocaBB;
9763 OI->OuterAllocBB = AllocaIP.getBlock();
9764
9765 // Add the thread ID argument.
9767 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9768 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9769
9770 // Generate the task body which will subsequently be outlined.
9771 Builder.restoreIP(TargetTaskBodyIP);
9772 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9773 return Err;
9774
9775 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9776 // it is given. These blocks are enumerated by
9777 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9778 // to be outside the region. In other words, OI.ExitBlock is expected to be
9779 // the start of the region after the outlining. We used to set OI.ExitBlock
9780 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9781 // except when the task body is a single basic block. In that case,
9782 // OI.ExitBlock is set to the single task body block and will get left out of
9783 // the outlining process. So, simply create a new empty block to which we
9784 // uncoditionally branch from where TaskBodyCB left off
9785 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9786 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9787 /*IsFinished=*/true);
9788
9789 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9790 bool NeedsTargetTask = HasNoWait && DeviceID;
9791 if (NeedsTargetTask) {
9792 for (auto *V :
9793 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9794 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9795 RTArgs.SizesArray}) {
9797 OffloadingArraysToPrivatize.push_back(V);
9798 OI->ExcludeArgsFromAggregate.push_back(V);
9799 }
9800 }
9801 }
9802 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9803 DeviceID, OffloadingArraysToPrivatize](
9804 Function &OutlinedFn) mutable {
9805 assert(OutlinedFn.hasOneUse() &&
9806 "there must be a single user for the outlined function");
9807
9808 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9809
9810 // The first argument of StaleCI is always the thread id.
9811 // The next few arguments are the pointers to offloading arrays
9812 // if any. (see OffloadingArraysToPrivatize)
9813 // Finally, all other local values that are live-in into the outlined region
9814 // end up in a structure whose pointer is passed as the last argument. This
9815 // piece of data is passed in the "shared" field of the task structure. So,
9816 // we know we have to pass shareds to the task if the number of arguments is
9817 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9818 // thread id. Further, for safety, we assert that the number of arguments of
9819 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9820 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9821 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9822 assert((!HasShareds ||
9823 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9824 "Wrong number of arguments for StaleCI when shareds are present");
9825 int SharedArgOperandNo =
9826 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9827
9828 StructType *TaskWithPrivatesTy =
9829 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9830 StructType *PrivatesTy = nullptr;
9831
9832 if (!OffloadingArraysToPrivatize.empty())
9833 PrivatesTy =
9834 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9835
9837 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9838 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9839
9840 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9841 << "\n");
9842
9843 Builder.SetInsertPoint(StaleCI);
9844
9845 // Gather the arguments for emitting the runtime call.
9846 uint32_t SrcLocStrSize;
9847 Constant *SrcLocStr =
9849 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9850
9851 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9852 //
9853 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9854 // the DeviceID to the deferred task and also since
9855 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9856 Function *TaskAllocFn =
9857 !NeedsTargetTask
9858 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9860 OMPRTL___kmpc_omp_target_task_alloc);
9861
9862 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9863 // call.
9864 Value *ThreadID = getOrCreateThreadID(Ident);
9865
9866 // Argument - `sizeof_kmp_task_t` (TaskSize)
9867 // Tasksize refers to the size in bytes of kmp_task_t data structure
9868 // plus any other data to be passed to the target task, if any, which
9869 // is packed into a struct. kmp_task_t and the struct so created are
9870 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9871 Value *TaskSize = Builder.getInt64(
9872 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9873
9874 // Argument - `sizeof_shareds` (SharedsSize)
9875 // SharedsSize refers to the shareds array size in the kmp_task_t data
9876 // structure.
9877 Value *SharedsSize = Builder.getInt64(0);
9878 if (HasShareds) {
9879 auto *ArgStructAlloca =
9880 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9881 assert(ArgStructAlloca &&
9882 "Unable to find the alloca instruction corresponding to arguments "
9883 "for extracted function");
9884 std::optional<TypeSize> ArgAllocSize =
9885 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9886 assert(ArgAllocSize &&
9887 "Unable to determine size of arguments for extracted function");
9888 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9889 }
9890
9891 // Argument - `flags`
9892 // Task is tied iff (Flags & 1) == 1.
9893 // Task is untied iff (Flags & 1) == 0.
9894 // Task is final iff (Flags & 2) == 2.
9895 // Task is not final iff (Flags & 2) == 0.
9896 // A target task is not final and is untied.
9897 Value *Flags = Builder.getInt32(0);
9898
9899 // Emit the @__kmpc_omp_task_alloc runtime call
9900 // The runtime call returns a pointer to an area where the task captured
9901 // variables must be copied before the task is run (TaskData)
9902 CallInst *TaskData = nullptr;
9903
9904 SmallVector<llvm::Value *> TaskAllocArgs = {
9905 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
9906 /*flags=*/Flags,
9907 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
9908 /*task_func=*/ProxyFn};
9909
9910 if (NeedsTargetTask) {
9911 assert(DeviceID && "Expected non-empty device ID.");
9912 TaskAllocArgs.push_back(DeviceID);
9913 }
9914
9915 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
9916
9917 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
9918 if (HasShareds) {
9919 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
9921 *this, Builder, TaskData, TaskWithPrivatesTy);
9922 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
9923 SharedsSize);
9924 }
9925 if (!OffloadingArraysToPrivatize.empty()) {
9926 Value *Privates =
9927 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
9928 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
9929 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
9930 [[maybe_unused]] Type *ArrayType =
9931 getOffloadingArrayType(PtrToPrivatize);
9932 assert(ArrayType && "ArrayType cannot be nullptr");
9933
9934 Type *ElementType = PrivatesTy->getElementType(i);
9935 assert(ElementType == ArrayType &&
9936 "ElementType should match ArrayType");
9937 (void)ArrayType;
9938
9939 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
9940 Builder.CreateMemCpy(
9941 Dst, Alignment, PtrToPrivatize, Alignment,
9942 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
9943 }
9944 }
9945
9946 Value *DepArray = nullptr;
9947 Value *NumDeps = nullptr;
9948 if (Dependencies.DepArray) {
9949 DepArray = Dependencies.DepArray;
9950 NumDeps = Dependencies.NumDeps;
9951 } else if (!Dependencies.Deps.empty()) {
9952 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
9953 NumDeps = Builder.getInt32(Dependencies.Deps.size());
9954 }
9955
9956 // ---------------------------------------------------------------
9957 // V5.2 13.8 target construct
9958 // If the nowait clause is present, execution of the target task
9959 // may be deferred. If the nowait clause is not present, the target task is
9960 // an included task.
9961 // ---------------------------------------------------------------
9962 // The above means that the lack of a nowait on the target construct
9963 // translates to '#pragma omp task if(0)'
9964 if (!NeedsTargetTask) {
9965 if (DepArray) {
9966 Function *TaskWaitFn =
9967 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
9969 TaskWaitFn,
9970 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
9971 /*ndeps=*/NumDeps,
9972 /*dep_list=*/DepArray,
9973 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
9974 /*noalias_dep_list=*/
9976 }
9977 // Included task.
9978 Function *TaskBeginFn =
9979 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
9980 Function *TaskCompleteFn =
9981 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
9982 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
9983 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
9984 CI->setDebugLoc(StaleCI->getDebugLoc());
9985 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
9986 } else if (DepArray) {
9987 // HasNoWait - meaning the task may be deferred. Call
9988 // __kmpc_omp_task_with_deps if there are dependencies,
9989 // else call __kmpc_omp_task
9990 Function *TaskFn =
9991 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
9993 TaskFn,
9994 {Ident, ThreadID, TaskData, NumDeps, DepArray,
9995 ConstantInt::get(Builder.getInt32Ty(), 0),
9997 } else {
9998 // Emit the @__kmpc_omp_task runtime call to spawn the task
9999 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
10000 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
10001 }
10002
10003 Builder.ClearInsertionPoint();
10004 StaleCI->eraseFromParent();
10005 for (Instruction *I : llvm::reverse(ToBeDeleted))
10006 I->eraseFromParent();
10007 };
10008 addOutlineInfo(std::move(OI));
10009
10010 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
10011 << *(Builder.GetInsertBlock()) << "\n");
10012 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
10013 << *(Builder.GetInsertBlock()->getParent()->getParent())
10014 << "\n");
10015 return Builder.saveIP();
10016}
10017
10019 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10020 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10021 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10022 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10023 if (Error Err =
10024 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10025 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10026 return Err;
10027 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10028 return Error::success();
10029}
10030
10031static void emitTargetCall(
10032 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
10037 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10041 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10042 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10043 // Generate a function call to the host fallback implementation of the target
10044 // region. This is called by the host when no offload entry was generated for
10045 // the target region and when the offloading call fails at runtime.
10046 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10048 Builder.restoreIP(IP);
10049 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10050 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10051 FallbackArgs.push_back(
10052 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10053 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10054 return Builder.saveIP();
10055 };
10056
10057 bool HasDependencies = !Dependencies.empty();
10058 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10059
10061
10062 auto TaskBodyCB =
10063 [&](Value *DeviceID, Value *RTLoc,
10064 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10065 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10066 // produce any.
10068 // emitKernelLaunch makes the necessary runtime call to offload the
10069 // kernel. We then outline all that code into a separate function
10070 // ('kernel_launch_function' in the pseudo code above). This function is
10071 // then called by the target task proxy function (see
10072 // '@.omp_target_task_proxy_func' in the pseudo code above)
10073 // "@.omp_target_task_proxy_func' is generated by
10074 // emitTargetTaskProxyFunction.
10075 if (OutlinedFnID && DeviceID)
10076 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10077 EmitTargetCallFallbackCB, KArgs,
10078 DeviceID, RTLoc, TargetTaskAllocaIP);
10079
10080 // We only need to do the outlining if `DeviceID` is set to avoid calling
10081 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10082 // generating the `else` branch of an `if` clause.
10083 //
10084 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10085 // In this case, we execute the host implementation directly.
10086 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10087 }());
10088
10089 OMPBuilder.Builder.restoreIP(AfterIP);
10090 return Error::success();
10091 };
10092
10093 auto &&EmitTargetCallElse =
10094 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10096 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10097 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10098 // produce any.
10100 if (RequiresOuterTargetTask) {
10101 // Arguments that are intended to be directly forwarded to an
10102 // emitKernelLaunch call are pased as nullptr, since
10103 // OutlinedFnID=nullptr results in that call not being done.
10105 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10106 /*RTLoc=*/nullptr, AllocaIP,
10107 Dependencies, EmptyRTArgs, HasNoWait);
10108 }
10109 return EmitTargetCallFallbackCB(Builder.saveIP());
10110 }());
10111
10112 Builder.restoreIP(AfterIP);
10113 return Error::success();
10114 };
10115
10116 auto &&EmitTargetCallThen =
10117 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10119 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10120 Info.HasNoWait = HasNoWait;
10121 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10122
10124 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10125 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10126 /*IsNonContiguous=*/true,
10127 /*ForEndCall=*/false))
10128 return Err;
10129
10130 SmallVector<Value *, 3> NumTeamsC;
10131 for (auto [DefaultVal, RuntimeVal] :
10132 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10133 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10134 : Builder.getInt32(DefaultVal));
10135
10136 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10137 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10138 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10139 if (Clause)
10140 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10141 /*isSigned=*/false);
10142 return Clause;
10143 };
10144 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10145 if (Clause)
10146 Result =
10147 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10148 Result, Clause)
10149 : Clause;
10150 };
10151
10152 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10153 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10154 SmallVector<Value *, 3> NumThreadsC;
10155 Value *MaxThreadsClause =
10156 RuntimeAttrs.TeamsThreadLimit.size() == 1
10157 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads.front())
10158 : nullptr;
10159
10160 for (auto [TeamsVal, TargetVal] : zip_equal(
10161 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10162 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10163 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10164
10165 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10166 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10167
10168 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10169 }
10170
10171 unsigned NumTargetItems = Info.NumberOfPtrs;
10172 uint32_t SrcLocStrSize;
10173 Constant *SrcLocStr = OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10174 Value *RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10175 llvm::omp::IdentFlag(0), 0);
10176
10177 Value *TripCount = RuntimeAttrs.LoopTripCount
10178 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10179 Builder.getInt64Ty(),
10180 /*isSigned=*/false)
10181 : Builder.getInt64(0);
10182
10183 // Request zero groupprivate bytes by default.
10184 if (!DynCGroupMem)
10185 DynCGroupMem = Builder.getInt32(0);
10186
10188 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10189 HasNoWait, /*StrictBlocks=*/false, /*StrictThreads=*/false,
10190 DynCGroupMemFallback);
10191
10192 // Assume no error was returned because TaskBodyCB and
10193 // EmitTargetCallFallbackCB don't produce any.
10195 // The presence of certain clauses on the target directive require the
10196 // explicit generation of the target task.
10197 if (RequiresOuterTargetTask)
10198 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10199 RTLoc, AllocaIP, Dependencies,
10200 KArgs.RTArgs, Info.HasNoWait);
10201
10202 return OMPBuilder.emitKernelLaunch(
10203 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10204 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10205 }());
10206
10207 Builder.restoreIP(AfterIP);
10208 return Error::success();
10209 };
10210
10211 // If we don't have an ID for the target region, it means an offload entry
10212 // wasn't created. In this case we just run the host fallback directly and
10213 // ignore any potential 'if' clauses.
10214 if (!OutlinedFnID) {
10215 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10216 return;
10217 }
10218
10219 // If there's no 'if' clause, only generate the kernel launch code path.
10220 if (!IfCond) {
10221 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10222 return;
10223 }
10224
10225 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10226 EmitTargetCallElse, AllocaIP));
10227}
10228
10230 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10231 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10232 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10233 const TargetKernelDefaultAttrs &DefaultAttrs,
10234 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10235 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10238 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10239 bool HasNowait, Value *DynCGroupMem,
10240 OMPDynGroupprivateFallbackType DynCGroupMemFallback,
10241 DebugLoc OutlinedFnLoc) {
10242
10243 if (!updateToLocation(Loc))
10244 return InsertPointTy();
10245
10246 Builder.restoreIP(CodeGenIP);
10247
10248 Function *OutlinedFn;
10249 Constant *OutlinedFnID = nullptr;
10250 // The target region is outlined into its own function. The LLVM IR for
10251 // the target region itself is generated using the callbacks CBFunc
10252 // and ArgAccessorFuncCB
10254 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10255 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB, OutlinedFnLoc))
10256 return Err;
10257
10258 // If we are not on the target device, then we need to generate code
10259 // to make a remote call (offload) to the previously outlined function
10260 // that represents the target region. Do that now.
10261 if (!Config.isTargetDevice())
10262 emitTargetCall(*this, Builder, AllocaIP, DeallocBlocks, Info, DefaultAttrs,
10263 RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID, Inputs,
10264 GenMapInfoCB, CustomMapperCB, Dependencies, HasNowait,
10265 DynCGroupMem, DynCGroupMemFallback);
10266 return Builder.saveIP();
10267}
10268
10269std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10270 StringRef FirstSeparator,
10271 StringRef Separator) {
10272 SmallString<128> Buffer;
10273 llvm::raw_svector_ostream OS(Buffer);
10274 StringRef Sep = FirstSeparator;
10275 for (StringRef Part : Parts) {
10276 OS << Sep << Part;
10277 Sep = Separator;
10278 }
10279 return OS.str().str();
10280}
10281
10282std::string
10284 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10285 Config.separator());
10286}
10287
10289 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10290 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10291 if (Elem.second) {
10292 assert(Elem.second->getValueType() == Ty &&
10293 "OMP internal variable has different type than requested");
10294 } else {
10295 // TODO: investigate the appropriate linkage type used for the global
10296 // variable for possibly changing that to internal or private, or maybe
10297 // create different versions of the function for different OMP internal
10298 // variables.
10299 const DataLayout &DL = M.getDataLayout();
10300 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10301 // default global AS is 1.
10302 // See double-target-call-with-declare-target.f90 and
10303 // declare-target-vars-in-target-region.f90 libomptarget
10304 // tests.
10305 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10306 : M.getTargetTriple().isAMDGPU()
10307 ? 0
10308 : DL.getDefaultGlobalsAddressSpace();
10309 auto Linkage = this->M.getTargetTriple().isWasm()
10312 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10313 Constant::getNullValue(Ty), Elem.first(),
10314 /*InsertBefore=*/nullptr,
10315 GlobalValue::NotThreadLocal, AddressSpaceVal);
10316 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10317 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10318 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10319 Elem.second = GV;
10320 }
10321
10322 return Elem.second;
10323}
10324
10325Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10326 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10327 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10328 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10329}
10330
10332 LLVMContext &Ctx = Builder.getContext();
10333 Value *Null =
10334 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10335 Value *SizeGep =
10336 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10337 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10338 return SizePtrToInt;
10339}
10340
10343 std::string VarName) {
10344 llvm::Constant *MaptypesArrayInit =
10345 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10346 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10347 M, MaptypesArrayInit->getType(),
10348 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10349 VarName);
10350 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10351 return MaptypesArrayGlobal;
10352}
10353
10355 InsertPointTy AllocaIP,
10356 unsigned NumOperands,
10357 struct MapperAllocas &MapperAllocas) {
10358 if (!updateToLocation(Loc))
10359 return;
10360
10361 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10362 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10363 Builder.restoreIP(AllocaIP);
10364 AllocaInst *ArgsBase = Builder.CreateAlloca(
10365 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10366 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10367 ".offload_ptrs");
10368 AllocaInst *ArgSizes = Builder.CreateAlloca(
10369 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10371 MapperAllocas.ArgsBase = ArgsBase;
10372 MapperAllocas.Args = Args;
10373 MapperAllocas.ArgSizes = ArgSizes;
10374}
10375
10377 Function *MapperFunc, Value *SrcLocInfo,
10378 Value *MaptypesArg, Value *MapnamesArg,
10380 int64_t DeviceID, unsigned NumOperands) {
10381 if (!updateToLocation(Loc))
10382 return;
10383
10384 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10385 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10386 Value *ArgsBaseGEP =
10387 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10388 {Builder.getInt32(0), Builder.getInt32(0)});
10389 Value *ArgsGEP =
10390 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10391 {Builder.getInt32(0), Builder.getInt32(0)});
10392 Value *ArgSizesGEP =
10393 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10394 {Builder.getInt32(0), Builder.getInt32(0)});
10395 Value *NullPtr =
10396 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10397 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10398 Builder.getInt32(NumOperands),
10399 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10400 MaptypesArg, MapnamesArg, NullPtr});
10401}
10402
10404 TargetDataRTArgs &RTArgs,
10405 TargetDataInfo &Info,
10406 bool ForEndCall) {
10407 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10408 "expected region end call to runtime only when end call is separate");
10409 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10410 auto VoidPtrTy = UnqualPtrTy;
10411 auto VoidPtrPtrTy = UnqualPtrTy;
10412 auto Int64Ty = Type::getInt64Ty(M.getContext());
10413 auto Int64PtrTy = UnqualPtrTy;
10414
10415 if (!Info.NumberOfPtrs) {
10416 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10417 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10418 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10419 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10420 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10421 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10422 return;
10423 }
10424
10425 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10426 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10427 Info.RTArgs.BasePointersArray,
10428 /*Idx0=*/0, /*Idx1=*/0);
10429 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10430 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10431 /*Idx0=*/0,
10432 /*Idx1=*/0);
10433 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10434 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10435 /*Idx0=*/0, /*Idx1=*/0);
10436 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10437 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10438 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10439 : Info.RTArgs.MapTypesArray,
10440 /*Idx0=*/0,
10441 /*Idx1=*/0);
10442
10443 // Only emit the mapper information arrays if debug information is
10444 // requested.
10445 if (!Info.EmitDebug)
10446 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10447 else
10448 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10449 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10450 /*Idx0=*/0,
10451 /*Idx1=*/0);
10452 // If there is no user-defined mapper, set the mapper array to nullptr to
10453 // avoid an unnecessary data privatization
10454 if (!Info.HasMapper)
10455 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10456 else
10457 RTArgs.MappersArray =
10458 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10459}
10460
10462 InsertPointTy CodeGenIP,
10463 MapInfosTy &CombinedInfo,
10464 TargetDataInfo &Info) {
10466 CombinedInfo.NonContigInfo;
10467
10468 // Build an array of struct descriptor_dim and then assign it to
10469 // offload_args.
10470 //
10471 // struct descriptor_dim {
10472 // uint64_t offset;
10473 // uint64_t count;
10474 // uint64_t stride
10475 // };
10476 Type *Int64Ty = Builder.getInt64Ty();
10478 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10479 "struct.descriptor_dim");
10480
10481 enum { OffsetFD = 0, CountFD, StrideFD };
10482 // We need two index variable here since the size of "Dims" is the same as
10483 // the size of Components, however, the size of offset, count, and stride is
10484 // equal to the size of base declaration that is non-contiguous.
10485 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10486 // Skip emitting ir if dimension size is 1 since it cannot be
10487 // non-contiguous.
10488 if (NonContigInfo.Dims[I] == 1)
10489 continue;
10490 Builder.restoreIP(AllocaIP);
10491 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10492 AllocaInst *DimsAddr =
10493 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10494 Builder.restoreIP(CodeGenIP);
10495 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10496 unsigned RevIdx = EE - II - 1;
10497 Value *DimsLVal = Builder.CreateInBoundsGEP(
10498 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10499 // Offset
10500 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10501 Builder.CreateAlignedStore(
10502 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10503 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10504 // Count
10505 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10506 Builder.CreateAlignedStore(
10507 NonContigInfo.Counts[L][RevIdx], CountLVal,
10508 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10509 // Stride
10510 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10511 Builder.CreateAlignedStore(
10512 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10513 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10514 }
10515 // args[I] = &dims
10516 Builder.restoreIP(CodeGenIP);
10517 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10518 DimsAddr, Builder.getPtrTy());
10519 Value *P = Builder.CreateConstInBoundsGEP2_32(
10520 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10521 Info.RTArgs.PointersArray, 0, I);
10522 Builder.CreateAlignedStore(
10523 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10524 ++L;
10525 }
10526}
10527
10528void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10529 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10530 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10531 BasicBlock *ExitBB, bool IsInit) {
10532 StringRef Prefix = IsInit ? ".init" : ".del";
10533
10534 // Evaluate if this is an array section.
10536 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10537 Value *IsArray =
10538 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10539 Value *DeleteBit = Builder.CreateAnd(
10540 MapType,
10541 Builder.getInt64(
10542 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10543 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10544 Value *DeleteCond;
10545 Value *Cond;
10546 if (IsInit) {
10547 // base != begin?
10548 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10549 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10550 DeleteCond = Builder.CreateIsNull(
10551 DeleteBit,
10552 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10553 } else {
10554 Cond = IsArray;
10555 DeleteCond = Builder.CreateIsNotNull(
10556 DeleteBit,
10557 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10558 }
10559 Cond = Builder.CreateAnd(Cond, DeleteCond);
10560 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10561
10562 emitBlock(BodyBB, MapperFn);
10563 // Get the array size by multiplying element size and element number (i.e., \p
10564 // Size).
10565 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10566 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10567 // memory allocation/deletion purpose only.
10568 Value *MapTypeArg = Builder.CreateAnd(
10569 MapType,
10570 Builder.getInt64(
10571 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10572 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10573 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10574 MapTypeArg = Builder.CreateOr(
10575 MapTypeArg,
10576 Builder.getInt64(
10577 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10578 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10579
10580 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10581 // data structure.
10582 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10583 ArraySize, MapTypeArg, MapName};
10585 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10586 OffloadingArgs);
10587}
10588
10591 llvm::Value *BeginArg)>
10592 GenMapInfoCB,
10593 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10594 bool PreserveMemberOfFlags, bool PropagatePresentToPointee) {
10595 SmallVector<Type *> Params;
10596 Params.emplace_back(Builder.getPtrTy());
10597 Params.emplace_back(Builder.getPtrTy());
10598 Params.emplace_back(Builder.getPtrTy());
10599 Params.emplace_back(Builder.getInt64Ty());
10600 Params.emplace_back(Builder.getInt64Ty());
10601 Params.emplace_back(Builder.getPtrTy());
10602
10603 auto *FnTy =
10604 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10605
10606 SmallString<64> TyStr;
10607 raw_svector_ostream Out(TyStr);
10608 Function *MapperFn =
10610 MapperFn->addFnAttr(Attribute::NoInline);
10611 MapperFn->addFnAttr(Attribute::NoUnwind);
10612 MapperFn->addParamAttr(0, Attribute::NoUndef);
10613 MapperFn->addParamAttr(1, Attribute::NoUndef);
10614 MapperFn->addParamAttr(2, Attribute::NoUndef);
10615 MapperFn->addParamAttr(3, Attribute::NoUndef);
10616 MapperFn->addParamAttr(4, Attribute::NoUndef);
10617 MapperFn->addParamAttr(5, Attribute::NoUndef);
10618
10619 // Start the mapper function code generation.
10620 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10622 Builder.SetInsertPoint(EntryBB);
10623 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
10624
10625 Value *MapperHandle = MapperFn->getArg(0);
10626 Value *BaseIn = MapperFn->getArg(1);
10627 Value *BeginIn = MapperFn->getArg(2);
10628 Value *Size = MapperFn->getArg(3);
10629 Value *MapType = MapperFn->getArg(4);
10630 Value *MapName = MapperFn->getArg(5);
10631
10632 // Compute the starting and end addresses of array elements.
10633 // Prepare common arguments for array initiation and deletion.
10634 // Convert the size in bytes into the number of array elements.
10635 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10636 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10637 Value *PtrBegin = BeginIn;
10638 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10639
10640 // Emit array initiation if this is an array section and \p MapType indicates
10641 // that memory allocation is required.
10642 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10643 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10644 MapType, MapName, ElementSize, HeadBB,
10645 /*IsInit=*/true);
10646
10647 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10648
10649 // Emit the loop header block.
10650 emitBlock(HeadBB, MapperFn);
10651 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10652 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10653 // Evaluate whether the initial condition is satisfied.
10654 Value *IsEmpty =
10655 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10656 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10657
10658 // Emit the loop body block.
10659 emitBlock(BodyBB, MapperFn);
10660 BasicBlock *LastBB = BodyBB;
10661 PHINode *PtrPHI =
10662 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10663 PtrPHI->addIncoming(PtrBegin, HeadBB);
10664
10665 // Get map clause information. Fill up the arrays with all mapped variables.
10666 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10667 if (!Info)
10668 return Info.takeError();
10669
10670 // Call the runtime API __tgt_mapper_num_components to get the number of
10671 // pre-existing components.
10672 Value *OffloadingArgs[] = {MapperHandle};
10673 Value *PreviousSize = createRuntimeFunctionCall(
10674 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10675 OffloadingArgs);
10676 Value *ShiftedPreviousSize =
10677 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10678
10679 // Fill up the runtime mapper handle for all components.
10680 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10681 Value *CurBaseArg = Info->BasePointers[I];
10682 Value *CurBeginArg = Info->Pointers[I];
10683 Value *CurSizeArg = Info->Sizes[I];
10684 Value *CurNameArg = Info->Names.size()
10685 ? Info->Names[I]
10686 : Constant::getNullValue(Builder.getPtrTy());
10687
10688 Value *OriMapType = Builder.getInt64(
10689 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10690 Info->Types[I]));
10691 auto RawType =
10692 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10693 Info->Types[I]);
10694 constexpr uint64_t MemberOfMask =
10695 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10696 constexpr uint64_t AttachBit =
10697 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10698 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10699
10700 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10701 // current array element (N = __tgt_mapper_num_components() at loop body
10702 // start).
10703 //
10704 // Example 1:
10705 // struct S { int x; int *p; };
10706 //
10707 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10708 // use: S arr[2]; ... map(arr)
10709 // entries per element:
10710 //
10711 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10712 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10713 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10714 //
10715 // Example 2:
10716 // struct S1 { int x; int y; };
10717 // struct S2 { int z; S1 *s1p; };
10718 //
10719 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10720 // s2.s1p->y)
10721 // use: S2 arr[2]; ... map(arr)
10722 // entries per element:
10723 //
10724 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10725 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10726 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10727 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10728 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10729 //
10730 // x/y carry inner MEMBER_OF(2)
10731 // which is shifted by N to become MEMBER_OF(N+2).
10732 //
10733 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10734 // the combined ALLOC entry for the s1p->x..y block, and the individual
10735 // x/y entries that are MEMBER_OF that block, all describe storage
10736 // reached through the attach ptr arr[i].s1p.
10737 //
10738 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10739 // linking them to the parent struct:
10740 //
10741 // * (*) Entries with HasAttachPtr: they represent pointee data that
10742 // occupies a different storage block than the struct being mapped, so
10743 // they are not a member of it. They may still be MEMBER_OF an entry
10744 // within that pointee block, in which case those pre-existing bits are
10745 // shifted -- see (***).
10746 // * (**) ATTACH entries: they are not a member of anything — they just
10747 // link a ptr to its ptee.
10748 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10749 // its pre-shaped entries already carry their final MEMBER_OF bits.
10750 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10751 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10752 // it.
10753 //
10754 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10755 // s1p->x/y entries above), those bits are still shifted by N.
10756 Value *MemberMapType;
10757 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10758 Info->HasAttachPtr[I]) {
10759 if (RawType & MemberOfMask)
10760 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10761 else
10762 MemberMapType = OriMapType;
10763 } else {
10764 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10765 }
10766
10767 // Combine the map type inherited from user-defined mapper with that
10768 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10769 // bits of the \a MapType, which is the input argument of the mapper
10770 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10771 // bits of MemberMapType.
10772 // [OpenMP 5.0], 1.2.6. map-type decay.
10773 // | alloc | to | from | tofrom | release | delete
10774 // ----------------------------------------------------------
10775 // alloc | alloc | alloc | alloc | alloc | release | delete
10776 // to | alloc | to | alloc | to | release | delete
10777 // from | alloc | alloc | from | from | release | delete
10778 // tofrom | alloc | to | from | tofrom | release | delete
10779 Value *LeftToFrom = Builder.CreateAnd(
10780 MapType,
10781 Builder.getInt64(
10782 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10783 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10784 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10785 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10786 BasicBlock *AllocElseBB =
10787 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10788 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10789 BasicBlock *ToElseBB =
10790 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10791 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10792 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10793 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10794 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10795 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10796 emitBlock(AllocBB, MapperFn);
10797 Value *AllocMapType = Builder.CreateAnd(
10798 MemberMapType,
10799 Builder.getInt64(
10800 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10801 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10802 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10803 Builder.CreateBr(EndBB);
10804 emitBlock(AllocElseBB, MapperFn);
10805 Value *IsTo = Builder.CreateICmpEQ(
10806 LeftToFrom,
10807 Builder.getInt64(
10808 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10809 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10810 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10811 // In case of to, clear OMP_MAP_FROM.
10812 emitBlock(ToBB, MapperFn);
10813 Value *ToMapType = Builder.CreateAnd(
10814 MemberMapType,
10815 Builder.getInt64(
10816 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10817 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10818 Builder.CreateBr(EndBB);
10819 emitBlock(ToElseBB, MapperFn);
10820 Value *IsFrom = Builder.CreateICmpEQ(
10821 LeftToFrom,
10822 Builder.getInt64(
10823 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10824 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10825 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10826 // In case of from, clear OMP_MAP_TO.
10827 emitBlock(FromBB, MapperFn);
10828 Value *FromMapType = Builder.CreateAnd(
10829 MemberMapType,
10830 Builder.getInt64(
10831 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10832 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10833 // In case of tofrom, do nothing.
10834 emitBlock(EndBB, MapperFn);
10835 LastBB = EndBB;
10836 PHINode *CurMapType =
10837 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10838 CurMapType->addIncoming(AllocMapType, AllocBB);
10839 CurMapType->addIncoming(ToMapType, ToBB);
10840 CurMapType->addIncoming(FromMapType, FromBB);
10841 CurMapType->addIncoming(MemberMapType, ToElseBB);
10842
10843 // Propagate map-type-modifying bits from the outer map clause to each map
10844 // inserted by the mapper.
10845 //
10846 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10847 // list item from the map clause and to apply the clauses specified in the
10848 // declared mapper to the construct on which the map clause appears...
10849 // If any modifier with the map-type-modifying property appears in the map
10850 // clause then the effect is as if that modifier appears in each map clause
10851 // specified in the declared mapper.
10852 //
10853 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10854 //
10855 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
10856 //
10857 // PRESENT is propagated only to entries that have an attach ptr
10858 // (HasAttachPtr): the pointee data, which occupies a different storage
10859 // block than the struct being mapped and so is not covered by the
10860 // present-check on the struct's own storage. A present modifier on the
10861 // outer clause must still require that pointee to be present on the device.
10862 //
10863 // This is gated on \p PropagatePresentToPointee (set by callers only for
10864 // OpenMP >= 6.0). Before 6.0 the present modifier is treated as not
10865 // applying to the pointee: the spec committee confirmed the divergence
10866 // between the present "motion" modifier (to/from) and the present map-type
10867 // modifier (map) was unintentional, to be fixed as an OpenMP 6.0 erratum,
10868 // so for 5.2 present is ignored for the pointee for both map and to/from.
10869 //
10870 // TODO: PRESENT should also be propagated to the struct's own members
10871 // (e.g. the s.x, s.y of map(present, mapper(id): s)) so that an absent
10872 // member triggers the present-check. We cannot do that yet: while pointer
10873 // members are mapped with PTR_AND_OBJ, a single combined entry allocates
10874 // the whole struct (including the pointer's storage), so propagating
10875 // PRESENT to it would wrongly require the pointer's pointee to be present.
10876 // Enable member propagation once Clang stops emitting PTR_AND_OBJ and uses
10877 // attach-style maps throughout.
10878 uint64_t ModifierBits =
10879 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10880 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
10881 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
10882 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
10883 if (PropagatePresentToPointee && Info->HasAttachPtr[I])
10884 ModifierBits |=
10885 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10886 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10887 Value *ImportedModifierBits =
10888 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
10889 Value *CurMapTypeWithModifiers = Builder.CreateOr(
10890 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
10891
10892 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
10893 // reserved for the attach(always) map-type modifier, and other modifier
10894 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
10895 Value *FinalMapType =
10896 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
10897
10898 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
10899 CurSizeArg, FinalMapType, CurNameArg};
10900
10901 auto ChildMapperFn = CustomMapperCB(I);
10902 if (!ChildMapperFn)
10903 return ChildMapperFn.takeError();
10904 if (*ChildMapperFn) {
10905 // Call the corresponding mapper function.
10906 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
10907 ->setDoesNotThrow();
10908 } else {
10909 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10910 // data structure.
10912 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10913 OffloadingArgs);
10914 }
10915 }
10916
10917 // Update the pointer to point to the next element that needs to be mapped,
10918 // and check whether we have mapped all elements.
10919 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
10920 "omp.arraymap.next");
10921 PtrPHI->addIncoming(PtrNext, LastBB);
10922 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
10923 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
10924 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
10925
10926 emitBlock(ExitBB, MapperFn);
10927 // Emit array deletion if this is an array section and \p MapType indicates
10928 // that deletion is required.
10929 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10930 MapType, MapName, ElementSize, DoneBB,
10931 /*IsInit=*/false);
10932
10933 // Emit the function exit block.
10934 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
10935
10936 Builder.CreateRetVoid();
10937 return MapperFn;
10938}
10939
10941 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
10942 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
10943 bool IsNonContiguous,
10944 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10945
10946 // Reset the array information.
10947 Info.clearArrayInfo();
10948 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
10949
10950 if (Info.NumberOfPtrs == 0)
10951 return Error::success();
10952
10953 Builder.restoreIP(AllocaIP);
10954 // Detect if we have any capture size requiring runtime evaluation of the
10955 // size so that a constant array could be eventually used.
10956 ArrayType *PointerArrayType =
10957 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
10958
10959 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
10960 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
10961
10962 Info.RTArgs.PointersArray = Builder.CreateAlloca(
10963 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
10964 AllocaInst *MappersArray = Builder.CreateAlloca(
10965 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
10966 Info.RTArgs.MappersArray = MappersArray;
10967
10968 // If we don't have any VLA types or other types that require runtime
10969 // evaluation, we can use a constant array for the map sizes, otherwise we
10970 // need to fill up the arrays as we do for the pointers.
10971 Type *Int64Ty = Builder.getInt64Ty();
10972 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
10973 ConstantInt::get(Int64Ty, 0));
10974 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
10975 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
10976 bool IsNonContigEntry =
10977 IsNonContiguous &&
10978 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10979 CombinedInfo.Types[I] &
10980 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
10981 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
10982 // descriptor_dim records), not the byte size.
10983 if (IsNonContigEntry) {
10984 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
10985 "Index must be in-bounds for NON_CONTIG Dims array");
10986 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
10987 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
10988 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
10989 continue;
10990 }
10991 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
10992 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
10993 ConstSizes[I] = CI;
10994 continue;
10995 }
10996 }
10997 RuntimeSizes.set(I);
10998 }
10999
11000 if (RuntimeSizes.all()) {
11001 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11002 Info.RTArgs.SizesArray = Builder.CreateAlloca(
11003 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11004 restoreIPandDebugLoc(Builder, CodeGenIP);
11005 } else {
11006 auto *SizesArrayInit = ConstantArray::get(
11007 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
11008 std::string Name = createPlatformSpecificName({"offload_sizes"});
11009 auto *SizesArrayGbl =
11010 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
11011 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
11012 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
11013
11014 if (!RuntimeSizes.any()) {
11015 Info.RTArgs.SizesArray = SizesArrayGbl;
11016 } else {
11017 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11018 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
11019 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11020 AllocaInst *Buffer = Builder.CreateAlloca(
11021 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11022 Buffer->setAlignment(OffloadSizeAlign);
11023 restoreIPandDebugLoc(Builder, CodeGenIP);
11024 Builder.CreateMemCpy(
11025 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11026 SizesArrayGbl, OffloadSizeAlign,
11027 Builder.getIntN(
11028 IndexSize,
11029 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11030
11031 Info.RTArgs.SizesArray = Buffer;
11032 }
11033 restoreIPandDebugLoc(Builder, CodeGenIP);
11034 }
11035
11036 // The map types are always constant so we don't need to generate code to
11037 // fill arrays. Instead, we create an array constant.
11039 for (auto mapFlag : CombinedInfo.Types)
11040 Mapping.push_back(
11041 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11042 mapFlag));
11043 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11044 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11045 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11046
11047 // The information types are only built if provided.
11048 if (!CombinedInfo.Names.empty()) {
11049 auto *MapNamesArrayGbl = createOffloadMapnames(
11050 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11051 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11052 Info.EmitDebug = true;
11053 } else {
11054 Info.RTArgs.MapNamesArray =
11056 Info.EmitDebug = false;
11057 }
11058
11059 // If there's a present map type modifier, it must not be applied to the end
11060 // of a region, so generate a separate map type array in that case.
11061 if (Info.separateBeginEndCalls()) {
11062 bool EndMapTypesDiffer = false;
11063 for (uint64_t &Type : Mapping) {
11064 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11065 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11066 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11067 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11068 EndMapTypesDiffer = true;
11069 }
11070 }
11071 if (EndMapTypesDiffer) {
11072 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11073 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11074 }
11075 }
11076
11077 PointerType *PtrTy = Builder.getPtrTy();
11078 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11079 Value *BPVal = CombinedInfo.BasePointers[I];
11080 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11081 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11082 0, I);
11083 Builder.CreateAlignedStore(BPVal, BP,
11084 M.getDataLayout().getPrefTypeAlign(PtrTy));
11085
11086 if (Info.requiresDevicePointerInfo()) {
11087 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11088 CodeGenIP = Builder.saveIP();
11089 Builder.restoreIP(AllocaIP);
11090 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11091 restoreIPandDebugLoc(Builder, CodeGenIP);
11092 if (DeviceAddrCB)
11093 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11094 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11095 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11096 if (DeviceAddrCB)
11097 DeviceAddrCB(I, BP);
11098 }
11099 }
11100
11101 Value *PVal = CombinedInfo.Pointers[I];
11102 Value *P = Builder.CreateConstInBoundsGEP2_32(
11103 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11104 I);
11105 // TODO: Check alignment correct.
11106 Builder.CreateAlignedStore(PVal, P,
11107 M.getDataLayout().getPrefTypeAlign(PtrTy));
11108
11109 if (RuntimeSizes.test(I)) {
11110 Value *S = Builder.CreateConstInBoundsGEP2_32(
11111 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11112 /*Idx0=*/0,
11113 /*Idx1=*/I);
11114 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11115 Int64Ty,
11116 /*isSigned=*/true),
11117 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11118 }
11119 // Fill up the mapper array.
11120 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11121 Value *MFunc = ConstantPointerNull::get(PtrTy);
11122
11123 auto CustomMFunc = CustomMapperCB(I);
11124 if (!CustomMFunc)
11125 return CustomMFunc.takeError();
11126 if (*CustomMFunc)
11127 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11128
11129 Value *MAddr = Builder.CreateInBoundsGEP(
11130 PointerArrayType, MappersArray,
11131 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11132 Builder.CreateAlignedStore(
11133 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11134 }
11135
11136 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11137 Info.NumberOfPtrs == 0)
11138 return Error::success();
11139 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11140 return Error::success();
11141}
11142
11144 BasicBlock *CurBB = Builder.GetInsertBlock();
11145
11146 if (!CurBB || CurBB->hasTerminator()) {
11147 // If there is no insert point or the previous block is already
11148 // terminated, don't touch it.
11149 } else {
11150 // Otherwise, create a fall-through branch.
11151 Builder.CreateBr(Target);
11152 }
11153
11154 Builder.ClearInsertionPoint();
11155}
11156
11158 bool IsFinished) {
11159 BasicBlock *CurBB = Builder.GetInsertBlock();
11160
11161 // Fall out of the current block (if necessary).
11162 emitBranch(BB);
11163
11164 if (IsFinished && BB->use_empty()) {
11165 BB->eraseFromParent();
11166 return;
11167 }
11168
11169 // Place the block after the current block, if possible, or else at
11170 // the end of the function.
11171 if (CurBB && CurBB->getParent())
11172 CurFn->insert(std::next(CurBB->getIterator()), BB);
11173 else
11174 CurFn->insert(CurFn->end(), BB);
11175 Builder.SetInsertPoint(BB);
11176}
11177
11179 BodyGenCallbackTy ElseGen,
11180 InsertPointTy AllocaIP,
11181 ArrayRef<BasicBlock *> DeallocBlocks) {
11182 // If the condition constant folds and can be elided, try to avoid emitting
11183 // the condition and the dead arm of the if/else.
11184 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11185 auto CondConstant = CI->getSExtValue();
11186 if (CondConstant)
11187 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11188
11189 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11190 }
11191
11192 Function *CurFn = Builder.GetInsertBlock()->getParent();
11193
11194 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11195 // emit the conditional branch.
11196 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11197 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11198 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11199 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11200 // Emit the 'then' code.
11201 emitBlock(ThenBlock, CurFn);
11202 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11203 return Err;
11204 emitBranch(ContBlock);
11205 // Emit the 'else' code if present.
11206 // There is no need to emit line number for unconditional branch.
11207 emitBlock(ElseBlock, CurFn);
11208 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11209 return Err;
11210 // There is no need to emit line number for unconditional branch.
11211 emitBranch(ContBlock);
11212 // Emit the continuation block for code after the if.
11213 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11214 return Error::success();
11215}
11216
11217bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11218 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11221 "Unexpected Atomic Ordering.");
11222
11223 bool Flush = false;
11225
11226 switch (AK) {
11227 case Read:
11230 FlushAO = AtomicOrdering::Acquire;
11231 Flush = true;
11232 }
11233 break;
11234 case Write:
11235 case Compare:
11236 case Update:
11239 FlushAO = AtomicOrdering::Release;
11240 Flush = true;
11241 }
11242 break;
11243 case Capture:
11244 switch (AO) {
11246 FlushAO = AtomicOrdering::Acquire;
11247 Flush = true;
11248 break;
11250 FlushAO = AtomicOrdering::Release;
11251 Flush = true;
11252 break;
11256 Flush = true;
11257 break;
11258 default:
11259 // do nothing - leave silently.
11260 break;
11261 }
11262 }
11263
11264 if (Flush) {
11265 // Currently Flush RT call still doesn't take memory_ordering, so for when
11266 // that happens, this tries to do the resolution of which atomic ordering
11267 // to use with but issue the flush call
11268 // TODO: pass `FlushAO` after memory ordering support is added
11269 (void)FlushAO;
11270 emitFlush(Loc);
11271 }
11272
11273 // for AO == AtomicOrdering::Monotonic and all other case combinations
11274 // do nothing
11275 return Flush;
11276}
11277
11281 AtomicOrdering AO, InsertPointTy AllocaIP) {
11282 if (!updateToLocation(Loc))
11283 return Loc.IP;
11284
11285 assert(X.Var->getType()->isPointerTy() &&
11286 "OMP Atomic expects a pointer to target memory");
11287 Type *XElemTy = X.ElemTy;
11288 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11289 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11290 "OMP atomic read expected a scalar type");
11291
11292 Value *XRead = nullptr;
11293
11294 if (XElemTy->isIntegerTy()) {
11295 LoadInst *XLD =
11296 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11297 XLD->setAtomic(AO);
11298 XRead = cast<Value>(XLD);
11299 } else if (XElemTy->isStructTy()) {
11300 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11301 // target does not support `atomicrmw` of the size of the struct
11302 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11303 OldVal->setAtomic(AO);
11304 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11305 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11306 OpenMPIRBuilder::AtomicInfo atomicInfo(
11307 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11308 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11309 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11310 XRead = AtomicLoadRes.first;
11311 OldVal->eraseFromParent();
11312 } else {
11313 // We need to perform atomic op as integer
11314 IntegerType *IntCastTy =
11315 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11316 LoadInst *XLoad =
11317 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11318 XLoad->setAtomic(AO);
11319 if (XElemTy->isFloatingPointTy()) {
11320 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11321 } else {
11322 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11323 }
11324 }
11325 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11326 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11327 return Builder.saveIP();
11328}
11329
11332 AtomicOpValue &X, Value *Expr,
11333 AtomicOrdering AO, InsertPointTy AllocaIP) {
11334 if (!updateToLocation(Loc))
11335 return Loc.IP;
11336
11337 assert(X.Var->getType()->isPointerTy() &&
11338 "OMP Atomic expects a pointer to target memory");
11339 Type *XElemTy = X.ElemTy;
11340 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11341 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11342 "OMP atomic write expected a scalar type");
11343
11344 if (XElemTy->isIntegerTy()) {
11345 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11346 XSt->setAtomic(AO);
11347 } else if (XElemTy->isStructTy()) {
11348 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11349 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11350 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11351 OpenMPIRBuilder::AtomicInfo atomicInfo(
11352 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11353 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11354 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11355 OldVal->eraseFromParent();
11356 } else {
11357 // We need to bitcast and perform atomic op as integers
11358 IntegerType *IntCastTy =
11359 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11360 Value *ExprCast =
11361 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11362 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11363 XSt->setAtomic(AO);
11364 }
11365
11366 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11367 return Builder.saveIP();
11368}
11369
11372 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11373 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11374 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11375 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11376 if (!updateToLocation(Loc))
11377 return Loc.IP;
11378
11379 LLVM_DEBUG({
11380 Type *XTy = X.Var->getType();
11381 assert(XTy->isPointerTy() &&
11382 "OMP Atomic expects a pointer to target memory");
11383 Type *XElemTy = X.ElemTy;
11384 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11385 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11386 "OMP atomic update expected a scalar or struct type");
11387 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11388 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&
11389 "OpenMP atomic does not support LT or GT operations");
11390 });
11391
11392 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11393 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11394 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11395 if (!AtomicResult)
11396 return AtomicResult.takeError();
11397 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11398 return Builder.saveIP();
11399}
11400
11401// FIXME: Duplicating AtomicExpand
11402Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11403 AtomicRMWInst::BinOp RMWOp) {
11404 switch (RMWOp) {
11405 case AtomicRMWInst::Add:
11406 return Builder.CreateAdd(Src1, Src2);
11407 case AtomicRMWInst::Sub:
11408 return Builder.CreateSub(Src1, Src2);
11409 case AtomicRMWInst::And:
11410 return Builder.CreateAnd(Src1, Src2);
11412 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11413 case AtomicRMWInst::Or:
11414 return Builder.CreateOr(Src1, Src2);
11415 case AtomicRMWInst::Xor:
11416 return Builder.CreateXor(Src1, Src2);
11421 case AtomicRMWInst::Max:
11422 case AtomicRMWInst::Min:
11435 llvm_unreachable("Unsupported atomic update operation");
11436 }
11437 llvm_unreachable("Unsupported atomic update operation");
11438}
11439
11441 // Loads cannot use Release or AcquireRelease ordering. This load is
11442 // just the initial value for the cmpxchg loop; the cmpxchg itself
11443 // retains the original ordering.
11444 AtomicOrdering LoadAO = AO;
11445
11446 if (AO == AtomicOrdering::Release) {
11448 } else if (AO == AtomicOrdering::AcquireRelease) {
11449 LoadAO = AtomicOrdering::Acquire;
11450 }
11451
11452 return LoadAO;
11453}
11454
11455Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11456 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11458 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11459 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11460 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11461 bool emitRMWOp = false;
11462 switch (RMWOp) {
11463 case AtomicRMWInst::Add:
11464 case AtomicRMWInst::And:
11466 case AtomicRMWInst::Or:
11467 case AtomicRMWInst::Xor:
11469 emitRMWOp = XElemTy;
11470 break;
11471 case AtomicRMWInst::Sub:
11472 emitRMWOp = (IsXBinopExpr && XElemTy);
11473 break;
11474 default:
11475 emitRMWOp = false;
11476 }
11477 emitRMWOp &= XElemTy->isIntegerTy();
11478
11479 std::pair<Value *, Value *> Res;
11480 if (emitRMWOp) {
11481 AtomicRMWInst *RMWInst =
11482 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11483 if (T.isAMDGPU()) {
11484 if (IsIgnoreDenormalMode)
11485 RMWInst->setMetadata("amdgpu.ignore.denormal.mode",
11486 llvm::MDNode::get(Builder.getContext(), {}));
11487 if (!IsFineGrainedMemory)
11488 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11489 llvm::MDNode::get(Builder.getContext(), {}));
11490 if (!IsRemoteMemory)
11491 RMWInst->setMetadata("amdgpu.no.remote.memory",
11492 llvm::MDNode::get(Builder.getContext(), {}));
11493 }
11494 Res.first = RMWInst;
11495 // not needed except in case of postfix captures. Generate anyway for
11496 // consistency with the else part. Will be removed with any DCE pass.
11497 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11498 if (RMWOp == AtomicRMWInst::Xchg)
11499 Res.second = Res.first;
11500 else
11501 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11502 } else if (XElemTy->isStructTy()) {
11503 LoadInst *OldVal =
11504 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11506 OldVal->setAtomic(LoadAO);
11507 const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
11508 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11509
11510 OpenMPIRBuilder::AtomicInfo atomicInfo(
11511 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11512 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11513 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11514 BasicBlock *CurBB = Builder.GetInsertBlock();
11515 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11516 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11517 BasicBlock *ExitBB =
11518 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11519 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11520 X->getName() + ".atomic.cont");
11521 ContBB->getTerminator()->eraseFromParent();
11522 Builder.restoreIP(AllocaIP);
11523 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11524 NewAtomicAddr->setName(X->getName() + "x.new.val");
11525 Builder.SetInsertPoint(ContBB);
11526 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11527 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11528 Value *OldExprVal = PHI;
11529 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11530 if (!CBResult)
11531 return CBResult.takeError();
11532 Value *Upd = *CBResult;
11533 Builder.CreateStore(Upd, NewAtomicAddr);
11536 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11537 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11538 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11539 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11540 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11541 OldVal->eraseFromParent();
11542 Res.first = OldExprVal;
11543 Res.second = Upd;
11544
11545 if (UnreachableInst *ExitTI =
11547 CurBBTI->eraseFromParent();
11548 Builder.SetInsertPoint(ExitBB);
11549 } else {
11550 Builder.SetInsertPoint(ExitTI);
11551 }
11552 } else {
11553 IntegerType *IntCastTy =
11554 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11555 LoadInst *OldVal =
11556 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11558 OldVal->setAtomic(LoadAO);
11559 // CurBB
11560 // | /---\
11561 // ContBB |
11562 // | \---/
11563 // ExitBB
11564 BasicBlock *CurBB = Builder.GetInsertBlock();
11565 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11566 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11567 BasicBlock *ExitBB =
11568 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11569 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11570 X->getName() + ".atomic.cont");
11571 ContBB->getTerminator()->eraseFromParent();
11572 Builder.restoreIP(AllocaIP);
11573 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11574 NewAtomicAddr->setName(X->getName() + "x.new.val");
11575 Builder.SetInsertPoint(ContBB);
11576 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11577 PHI->addIncoming(OldVal, CurBB);
11578 bool IsIntTy = XElemTy->isIntegerTy();
11579 Value *OldExprVal = PHI;
11580 if (!IsIntTy) {
11581 if (XElemTy->isFloatingPointTy()) {
11582 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11583 X->getName() + ".atomic.fltCast");
11584 } else {
11585 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11586 X->getName() + ".atomic.ptrCast");
11587 }
11588 }
11589
11590 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11591 if (!CBResult)
11592 return CBResult.takeError();
11593 Value *Upd = *CBResult;
11594 Builder.CreateStore(Upd, NewAtomicAddr);
11595 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11598 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11599 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11600 Result->setVolatile(VolatileX);
11601 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11602 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11603 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11604 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11605
11606 Res.first = OldExprVal;
11607 Res.second = Upd;
11608
11609 // set Insertion point in exit block
11610 if (UnreachableInst *ExitTI =
11612 CurBBTI->eraseFromParent();
11613 Builder.SetInsertPoint(ExitBB);
11614 } else {
11615 Builder.SetInsertPoint(ExitTI);
11616 }
11617 }
11618
11619 return Res;
11620}
11621
11624 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11625 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11626 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11627 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11628 if (!updateToLocation(Loc))
11629 return Loc.IP;
11630
11631 LLVM_DEBUG({
11632 Type *XTy = X.Var->getType();
11633 assert(XTy->isPointerTy() &&
11634 "OMP Atomic expects a pointer to target memory");
11635 Type *XElemTy = X.ElemTy;
11636 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11637 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11638 "OMP atomic capture expected a scalar or struct type");
11639 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11640 "OpenMP atomic does not support LT or GT operations");
11641 });
11642
11643 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11644 // 'x' is simply atomically rewritten with 'expr'.
11645 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11646 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11647 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11648 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11649 if (!AtomicResult)
11650 return AtomicResult.takeError();
11651 Value *CapturedVal =
11652 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11653 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11654
11655 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11656 return Builder.saveIP();
11657}
11658
11662 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11663 bool IsFailOnly, bool IsWeak) {
11664
11666 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11667 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11668}
11669
11673 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11674 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11675
11676 if (!updateToLocation(Loc))
11677 return Loc.IP;
11678
11679 assert(X.Var->getType()->isPointerTy() &&
11680 "OMP atomic expects a pointer to target memory");
11681 // compare capture
11682 if (V.Var) {
11683 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11684 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11685 }
11686
11687 bool IsInteger = E->getType()->isIntegerTy();
11688
11689 if (Op == OMPAtomicCompareOp::EQ) {
11690 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11691 // R.Var handling.
11692 Value *OldValue = nullptr;
11693 Value *SuccessOrFail = nullptr;
11694
11695 if (!IsInteger && HandleFPNegZero) {
11696 // IEEE 754 special cases for cmpxchg (which is bitwise):
11697 // 1. -0.0 == +0.0 but they have different bit patterns.
11698 // 2. NaN != NaN but identical NaN bit patterns would match.
11699 //
11700 // CurBB:
11701 // %e_int = bitcast E to intN
11702 // %d_int = bitcast D to intN
11703 // %x_curr = load atomic intN, X
11704 // %x_fp = bitcast %x_curr to FP
11705 // %e_is_nan = fcmp uno E, E
11706 // %x_is_nan = fcmp uno %x_fp, %x_fp
11707 // %either_nan = or %e_is_nan, %x_is_nan
11708 // br %either_nan, NaNBB, NotNaNBB
11709 // NaNBB: ; NaN == anything is always false
11710 // br ExitBB
11711 // NotNaNBB:
11712 // %x_is_zero = fcmp oeq %x_fp, 0.0
11713 // %e_is_zero = fcmp oeq E, 0.0
11714 // %both_zero = and %x_is_zero, %e_is_zero
11715 // br %both_zero, ZeroBB, NormalBB
11716 // ZeroBB: ; both ±0.0 → x = d
11717 // cmpxchg X, %x_curr, %d_int
11718 // br ExitBB
11719 // NormalBB: ; original path
11720 // cmpxchg X, %e_int, %d_int
11721 // br ExitBB
11722 // ExitBB:
11723 // phi merge
11724 IntegerType *IntCastTy =
11725 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11726 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11727 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11728
11729 // Load X atomically.
11730 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11731 X.Var->getName() + ".atomic.load");
11733 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11734
11735 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11736 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11737 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11738 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11739 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11740
11741 BasicBlock *CurBB = Builder.GetInsertBlock();
11742 Function *F = CurBB->getParent();
11743 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11744 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11745 BasicBlock *ExitBB =
11746 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11748 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11749 BasicBlock *NotNaNBB = BasicBlock::Create(
11750 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11752 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11753 BasicBlock *NormalBB = BasicBlock::Create(
11754 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11755
11756 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11757 CurBB->getTerminator()->eraseFromParent();
11758 Builder.SetInsertPoint(CurBB);
11759 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11760
11761 // NaNBB: NaN == anything is always false; skip cmpxchg.
11762 Builder.SetInsertPoint(NaNBB);
11763 Builder.CreateBr(ExitBB);
11764
11765 // NotNaNBB: check both X and E for ±0.0.
11766 Builder.SetInsertPoint(NotNaNBB);
11767 Value *XIsZero =
11768 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11769 X.Var->getName() + ".atomic.xiszero");
11770 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11771 "atomic.e.iszero");
11772 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11773 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11774
11775 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11776 Builder.SetInsertPoint(ZeroBB);
11777 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11778 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11779 ResZero->setWeak(IsWeak);
11780 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11781 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11782 Builder.CreateBr(ExitBB);
11783
11784 // NormalBB: original bitwise cmpxchg.
11785 Builder.SetInsertPoint(NormalBB);
11786 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11787 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11788 ResNormal->setWeak(IsWeak);
11789 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11790 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11791 Builder.CreateBr(ExitBB);
11792
11793 // ExitBB: merge results from NaN, Zero, and Normal paths.
11794 Builder.SetInsertPoint(ExitBB, ExitBB->begin());
11795 PHINode *OldIntPHI =
11796 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11797 OldIntPHI->addIncoming(XCurr, NaNBB);
11798 OldIntPHI->addIncoming(OldZero, ZeroBB);
11799 OldIntPHI->addIncoming(OldNormal, NormalBB);
11800 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11801 X.Var->getName() + ".atomic.ok");
11802 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11803 SuccessPHI->addIncoming(OkZero, ZeroBB);
11804 SuccessPHI->addIncoming(OkNormal, NormalBB);
11805
11806 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11807 CurBBTI->eraseFromParent();
11808 Builder.SetInsertPoint(ExitBB);
11809 } else {
11810 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11811 }
11812
11813 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11814 X.Var->getName() + ".atomic.old.fp");
11815 SuccessOrFail = SuccessPHI;
11816 } else {
11817 AtomicCmpXchgInst *Result = nullptr;
11818 if (!IsInteger) {
11819 IntegerType *IntCastTy =
11820 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11821 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11822 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11823 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11824 MaybeAlign(), AO, Failure);
11825 } else {
11826 Result =
11827 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11828 }
11829 Result->setWeak(IsWeak);
11830
11831 if (V.Var) {
11832 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11833 if (!IsInteger)
11834 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11835 assert(OldValue->getType() == V.ElemTy &&
11836 "OldValue and V must be of same type");
11837 if (IsPostfixUpdate) {
11838 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11839 } else {
11840 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11841 if (IsFailOnly) {
11842 BasicBlock *CurBB = Builder.GetInsertBlock();
11843 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11844 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11845 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11846 CurBBTI, X.Var->getName() + ".atomic.exit");
11847 BasicBlock *ContBB = CurBB->splitBasicBlock(
11848 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11849 ContBB->getTerminator()->eraseFromParent();
11850 CurBB->getTerminator()->eraseFromParent();
11851
11852 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11853
11854 Builder.SetInsertPoint(ContBB);
11855 Builder.CreateStore(OldValue, V.Var);
11856 Builder.CreateBr(ExitBB);
11857
11858 if (UnreachableInst *ExitTI =
11860 CurBBTI->eraseFromParent();
11861 Builder.SetInsertPoint(ExitBB);
11862 } else {
11863 Builder.SetInsertPoint(ExitTI);
11864 }
11865 } else {
11866 Value *CapturedValue =
11867 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11868 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11869 }
11870 }
11871 }
11872 // The comparison result has to be stored.
11873 if (R.Var) {
11874 assert(R.Var->getType()->isPointerTy() &&
11875 "r.var must be of pointer type");
11876 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11877
11878 Value *SuccessFailureVal =
11879 Builder.CreateExtractValue(Result, /*Idxs=*/1);
11880 Value *ResultCast =
11881 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
11882 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
11883 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11884 }
11885 }
11886
11887 // For the HandleFPNegZero path, handle V.Var and R.Var using the
11888 // pre-computed OldValue and SuccessOrFail.
11889 if (HandleFPNegZero && !IsInteger) {
11890 if (V.Var) {
11891 assert(OldValue->getType() == V.ElemTy &&
11892 "OldValue and V must be of same type");
11893 if (IsPostfixUpdate) {
11894 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11895 } else {
11896 if (IsFailOnly) {
11897 BasicBlock *CurBB = Builder.GetInsertBlock();
11898 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11899 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11900 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11901 CurBBTI, X.Var->getName() + ".atomic.exit");
11902 BasicBlock *ContBB = CurBB->splitBasicBlock(
11903 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11904 ContBB->getTerminator()->eraseFromParent();
11905 CurBB->getTerminator()->eraseFromParent();
11906
11907 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11908
11909 Builder.SetInsertPoint(ContBB);
11910 Builder.CreateStore(OldValue, V.Var);
11911 Builder.CreateBr(ExitBB);
11912
11913 if (UnreachableInst *ExitTI =
11915 CurBBTI->eraseFromParent();
11916 Builder.SetInsertPoint(ExitBB);
11917 } else {
11918 Builder.SetInsertPoint(ExitTI);
11919 }
11920 } else {
11921 Value *CapturedValue =
11922 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11923 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11924 }
11925 }
11926 }
11927 // The comparison result has to be stored.
11928 if (R.Var) {
11929 assert(R.Var->getType()->isPointerTy() &&
11930 "r.var must be of pointer type");
11931 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11932
11933 Value *ResultCast = R.IsSigned
11934 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
11935 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
11936 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11937 }
11938 }
11939 } else {
11940 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
11941 "Op should be either max or min at this point");
11942 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
11943
11944 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
11945 // Let's take max as example.
11946 // OpenMP form:
11947 // x = x > expr ? expr : x;
11948 // LLVM form:
11949 // *ptr = *ptr > val ? *ptr : val;
11950 // We need to transform to LLVM form.
11951 // x = x <= expr ? x : expr;
11953 if (IsXBinopExpr) {
11954 if (IsInteger) {
11955 if (X.IsSigned)
11956 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
11958 else
11959 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
11961 } else {
11962 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
11964 }
11965 } else {
11966 if (IsInteger) {
11967 if (X.IsSigned)
11968 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
11970 else
11971 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
11973 } else {
11974 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
11976 }
11977 }
11978
11979 AtomicRMWInst *OldValue =
11980 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
11981 if (V.Var) {
11982 Value *CapturedValue = nullptr;
11983 if (IsPostfixUpdate) {
11984 CapturedValue = OldValue;
11985 } else {
11986 CmpInst::Predicate Pred;
11987 switch (NewOp) {
11988 case AtomicRMWInst::Max:
11989 Pred = CmpInst::ICMP_SGT;
11990 break;
11992 Pred = CmpInst::ICMP_UGT;
11993 break;
11995 Pred = CmpInst::FCMP_OGT;
11996 break;
11997 case AtomicRMWInst::Min:
11998 Pred = CmpInst::ICMP_SLT;
11999 break;
12001 Pred = CmpInst::ICMP_ULT;
12002 break;
12004 Pred = CmpInst::FCMP_OLT;
12005 break;
12006 default:
12007 llvm_unreachable("unexpected comparison op");
12008 }
12009 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
12010 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
12011 }
12012 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12013 }
12014 }
12015
12016 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
12017
12018 return Builder.saveIP();
12019}
12020
12023 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12024 Value *NumTeamsUpper, Value *ThreadLimit,
12025 Value *IfExpr) {
12026 if (!updateToLocation(Loc))
12027 return InsertPointTy();
12028
12029 uint32_t SrcLocStrSize;
12030 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12031 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12032 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12033
12034 // Outer allocation basicblock is the entry block of the current function.
12035 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12036 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12037 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12038 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12039 }
12040
12041 // The current basic block is split into four basic blocks. After outlining,
12042 // they will be mapped as follows:
12043 // ```
12044 // def current_fn() {
12045 // current_basic_block:
12046 // br label %teams.exit
12047 // teams.exit:
12048 // ; instructions after teams
12049 // }
12050 //
12051 // def outlined_fn() {
12052 // teams.alloca:
12053 // br label %teams.body
12054 // teams.body:
12055 // ; instructions within teams body
12056 // }
12057 // ```
12058 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12059 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12060 BasicBlock *AllocaBB =
12061 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12062
12063 bool SubClausesPresent =
12064 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12065 // Push num_teams
12066 if (!Config.isTargetDevice() && SubClausesPresent) {
12067 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12068 "if lowerbound is non-null, then upperbound must also be non-null "
12069 "for bounds on num_teams");
12070
12071 if (NumTeamsUpper == nullptr)
12072 NumTeamsUpper = Builder.getInt32(0);
12073
12074 if (NumTeamsLower == nullptr)
12075 NumTeamsLower = NumTeamsUpper;
12076
12077 if (IfExpr) {
12078 assert(IfExpr->getType()->isIntegerTy() &&
12079 "argument to if clause must be an integer value");
12080
12081 // upper = ifexpr ? upper : 1
12082 if (IfExpr->getType() != Int1)
12083 IfExpr = Builder.CreateICmpNE(IfExpr,
12084 ConstantInt::get(IfExpr->getType(), 0));
12085 NumTeamsUpper = Builder.CreateSelect(
12086 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12087
12088 // lower = ifexpr ? lower : 1
12089 NumTeamsLower = Builder.CreateSelect(
12090 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12091 }
12092
12093 if (ThreadLimit == nullptr)
12094 ThreadLimit = Builder.getInt32(0);
12095
12096 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12097 // truncate or sign extend the passed values to match the int32 parameters.
12098 Value *NumTeamsLowerInt32 =
12099 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12100 Value *NumTeamsUpperInt32 =
12101 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12102 Value *ThreadLimitInt32 =
12103 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12104
12105 Value *ThreadNum = getOrCreateThreadID(Ident);
12106
12108 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12109 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12110 ThreadLimitInt32});
12111 }
12112 // Generate the body of teams.
12113 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12114 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12115 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12116 return Err;
12117
12118 auto OI = std::make_unique<OutlineInfo>();
12119 OI->EntryBB = AllocaBB;
12120 OI->ExitBB = ExitBB;
12121 OI->OuterAllocBB = &OuterAllocaBB;
12122
12123 // Insert fake values for global tid and bound tid.
12125 InsertPointTy OuterAllocaIP(&OuterAllocaBB, OuterAllocaBB.begin());
12126 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12127 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12128 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12129 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12130
12131 auto HostPostOutlineCB = [this, Ident,
12132 ToBeDeleted](Function &OutlinedFn) mutable {
12133 // The stale call instruction will be replaced with a new call instruction
12134 // for runtime call with the outlined function.
12135
12136 assert(OutlinedFn.hasOneUse() &&
12137 "there must be a single user for the outlined function");
12138 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12139 ToBeDeleted.push_back(StaleCI);
12140
12141 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12142 "Outlined function must have two or three arguments only");
12143
12144 bool HasShared = OutlinedFn.arg_size() == 3;
12145
12146 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12147 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12148 if (HasShared)
12149 OutlinedFn.getArg(2)->setName("data");
12150
12151 // Call to the runtime function for teams in the current function.
12152 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12153 "outlined function.");
12154 Builder.SetInsertPoint(StaleCI);
12155 SmallVector<Value *> Args = {
12156 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12157 if (HasShared)
12158 Args.push_back(StaleCI->getArgOperand(2));
12161 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12162 Args);
12163
12164 Builder.ClearInsertionPoint();
12165 for (Instruction *I : llvm::reverse(ToBeDeleted))
12166 I->eraseFromParent();
12167 };
12168
12169 if (!Config.isTargetDevice())
12170 OI->PostOutlineCB = HostPostOutlineCB;
12171
12172 addOutlineInfo(std::move(OI));
12173
12174 Builder.SetInsertPoint(ExitBB);
12175
12176 return Builder.saveIP();
12177}
12178
12180 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12181 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12182 if (!updateToLocation(Loc))
12183 return InsertPointTy();
12184
12185 BasicBlock *OuterAllocaBB = OuterAllocIP.getBlock();
12186
12187 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12188 BasicBlock *BodyBB =
12189 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12190 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12191 }
12192 BasicBlock *ExitBB =
12193 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12194 BasicBlock *BodyBB =
12195 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12196 BasicBlock *AllocaBB =
12197 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12198
12199 // Generate the body of distribute clause
12200 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12201 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12202 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12203 return Err;
12204
12205 // When using target we use different runtime functions which require a
12206 // callback.
12207 if (Config.isTargetDevice()) {
12208 auto OI = std::make_unique<OutlineInfo>();
12209 OI->OuterAllocBB = OuterAllocIP.getBlock();
12210 OI->EntryBB = AllocaBB;
12211 OI->ExitBB = ExitBB;
12212 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12213 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12214
12215 addOutlineInfo(std::move(OI));
12216 }
12217 Builder.SetInsertPoint(ExitBB);
12218
12219 return Builder.saveIP();
12220}
12221
12224 std::string VarName) {
12225 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12227 Names.size()),
12228 Names);
12229 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12230 M, MapNamesArrayInit->getType(),
12231 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12232 VarName);
12233 return MapNamesArrayGlobal;
12234}
12235
12236// Create all simple and struct types exposed by the runtime and remember
12237// the llvm::PointerTypes of them for easy access later.
12238void OpenMPIRBuilder::initializeTypes(Module &M) {
12239 LLVMContext &Ctx = M.getContext();
12240 StructType *T;
12241 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12242 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12243#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12244#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12245 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12246 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12247#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12248 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12249 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12250#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12251 T = StructType::getTypeByName(Ctx, StructName); \
12252 if (!T) \
12253 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12254 VarName = T; \
12255 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12256#include "llvm/Frontend/OpenMP/OMPKinds.def"
12257}
12258
12261 SmallVectorImpl<BasicBlock *> &BlockVector) {
12263 BlockSet.insert(EntryBB);
12264 BlockSet.insert(ExitBB);
12265
12266 Worklist.push_back(EntryBB);
12267 while (!Worklist.empty()) {
12268 BasicBlock *BB = Worklist.pop_back_val();
12269 BlockVector.push_back(BB);
12270 for (BasicBlock *SuccBB : successors(BB))
12271 if (BlockSet.insert(SuccBB).second)
12272 Worklist.push_back(SuccBB);
12273 }
12274}
12275
12276std::unique_ptr<CodeExtractor>
12278 bool ArgsInZeroAddressSpace,
12279 Twine Suffix) {
12280 return std::make_unique<CodeExtractor>(
12281 Blocks, /* DominatorTree */ nullptr,
12282 /* AggregateArgs */ true,
12283 /* BlockFrequencyInfo */ nullptr,
12284 /* BranchProbabilityInfo */ nullptr,
12285 /* AssumptionCache */ nullptr,
12286 /* AllowVarArgs */ true,
12287 /* AllowAlloca */ true,
12288 /* AllocationBlock*/ OuterAllocBB,
12289 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12290 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12291}
12292
12293std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12294 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12295 return std::make_unique<DeviceSharedMemCodeExtractor>(
12296 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12297 /* AggregateArgs */ true,
12298 /* BlockFrequencyInfo */ nullptr,
12299 /* BranchProbabilityInfo */ nullptr,
12300 /* AssumptionCache */ nullptr,
12301 /* AllowVarArgs */ true,
12302 /* AllowAlloca */ true,
12303 /* AllocationBlock*/ OuterAllocBB,
12304 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12306 : OuterDeallocBBs,
12307 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12308}
12309
12311 uint64_t Size, int32_t Flags,
12313 StringRef Name) {
12314 if (!Config.isGPU()) {
12317 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12318 return;
12319 }
12320 // TODO: Add support for global variables on the device after declare target
12321 // support.
12322 Function *Fn = dyn_cast<Function>(Addr);
12323 if (!Fn)
12324 return;
12325
12326 // Add a function attribute for the kernel.
12327 Fn->addFnAttr("kernel");
12328 if (T.isAMDGCN())
12329 Fn->addFnAttr("uniform-work-group-size");
12330 Fn->addFnAttr(Attribute::MustProgress);
12331}
12332
12333// We only generate metadata for function that contain target regions.
12336
12337 // If there are no entries, we don't need to do anything.
12338 if (OffloadInfoManager.empty())
12339 return;
12340
12341 LLVMContext &C = M.getContext();
12344 16>
12345 OrderedEntries(OffloadInfoManager.size());
12346
12347 // Auxiliary methods to create metadata values and strings.
12348 auto &&GetMDInt = [this](unsigned V) {
12349 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12350 };
12351
12352 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12353
12354 // Create the offloading info metadata node.
12355 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12356 auto &&TargetRegionMetadataEmitter =
12357 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12358 const TargetRegionEntryInfo &EntryInfo,
12360 // Generate metadata for target regions. Each entry of this metadata
12361 // contains:
12362 // - Entry 0 -> Kind of this type of metadata (0).
12363 // - Entry 1 -> Device ID of the file where the entry was identified.
12364 // - Entry 2 -> File ID of the file where the entry was identified.
12365 // - Entry 3 -> Mangled name of the function where the entry was
12366 // identified.
12367 // - Entry 4 -> Line in the file where the entry was identified.
12368 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12369 // - Entry 6 -> Order the entry was created.
12370 // The first element of the metadata node is the kind.
12371 Metadata *Ops[] = {
12372 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12373 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12374 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12375 GetMDInt(E.getOrder())};
12376
12377 // Save this entry in the right position of the ordered entries array.
12378 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12379
12380 // Add metadata to the named metadata node.
12381 MD->addOperand(MDNode::get(C, Ops));
12382 };
12383
12384 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12385
12386 // Create function that emits metadata for each device global variable entry;
12387 auto &&DeviceGlobalVarMetadataEmitter =
12388 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12389 StringRef MangledName,
12391 // Generate metadata for global variables. Each entry of this metadata
12392 // contains:
12393 // - Entry 0 -> Kind of this type of metadata (1).
12394 // - Entry 1 -> Mangled name of the variable.
12395 // - Entry 2 -> Declare target kind.
12396 // - Entry 3 -> Order the entry was created.
12397 // The first element of the metadata node is the kind.
12398 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12399 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12400
12401 // Save this entry in the right position of the ordered entries array.
12402 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12403 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12404
12405 // Add metadata to the named metadata node.
12406 MD->addOperand(MDNode::get(C, Ops));
12407 };
12408
12409 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12410 DeviceGlobalVarMetadataEmitter);
12411
12412 for (const auto &E : OrderedEntries) {
12413 assert(E.first && "All ordered entries must exist!");
12414 if (const auto *CE =
12416 E.first)) {
12417 if (!CE->getID() || !CE->getAddress()) {
12418 // Do not blame the entry if the parent funtion is not emitted.
12419 TargetRegionEntryInfo EntryInfo = E.second;
12420 StringRef FnName = EntryInfo.ParentName;
12421 if (!M.getNamedValue(FnName))
12422 continue;
12423 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12424 continue;
12425 }
12426 createOffloadEntry(CE->getID(), CE->getAddress(),
12427 /*Size=*/0, CE->getFlags(),
12429 } else if (const auto *CE = dyn_cast<
12431 E.first)) {
12434 CE->getFlags());
12435 switch (Flags) {
12438 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12439 continue;
12440 if (!CE->getAddress()) {
12441 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12442 continue;
12443 }
12444 // The vaiable has no definition - no need to add the entry.
12445 if (CE->getVarSize() == 0)
12446 continue;
12447 break;
12449 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12450 (!Config.isTargetDevice() && CE->getAddress())) &&
12451 "Declaret target link address is set.");
12452 if (Config.isTargetDevice())
12453 continue;
12454 if (!CE->getAddress()) {
12456 continue;
12457 }
12458 break;
12461 if (!CE->getAddress()) {
12462 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12463 continue;
12464 }
12465 break;
12466 default:
12467 break;
12468 }
12469
12470 // Hidden or internal symbols on the device are not externally visible.
12471 // We should not attempt to register them by creating an offloading
12472 // entry. Indirect variables are handled separately on the device.
12473 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12474 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12475 (Flags !=
12477 Flags != OffloadEntriesInfoManager::
12478 OMPTargetGlobalVarEntryIndirectVTable))
12479 continue;
12480
12481 // Indirect globals need to use a special name that doesn't match the name
12482 // of the associated host global.
12484 Flags ==
12486 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12487 Flags, CE->getLinkage(), CE->getVarName());
12488 else
12489 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12490 Flags, CE->getLinkage());
12491
12492 } else {
12493 llvm_unreachable("Unsupported entry kind.");
12494 }
12495 }
12496
12497 // Emit requires directive globals to a special entry so the runtime can
12498 // register them when the device image is loaded.
12499 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12500 // entries should be redesigned to better suit this use-case.
12501 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12505 ".requires", /*Size=*/0,
12507 Config.getRequiresFlags());
12508}
12509
12512 unsigned FileID, unsigned Line, unsigned Count) {
12513 raw_svector_ostream OS(Name);
12514 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12515 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12516 if (Count)
12517 OS << "_" << Count;
12518}
12519
12521 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12522 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12524 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12525 EntryInfo.Line, NewCount);
12526}
12527
12530 vfs::FileSystem &VFS,
12531 StringRef ParentName) {
12532 sys::fs::UniqueID ID(0xdeadf17e, 0);
12533 auto FileIDInfo = CallBack();
12534 uint64_t FileID = 0;
12535 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12536 ID = Status->getUniqueID();
12537 FileID = Status->getUniqueID().getFile();
12538 } else {
12539 // If the inode ID could not be determined, create a hash value
12540 // the current file name and use that as an ID.
12541 FileID = hash_value(std::get<0>(FileIDInfo));
12542 }
12543
12544 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12545 std::get<1>(FileIDInfo));
12546}
12547
12549 unsigned Offset = 0;
12550 for (uint64_t Remain =
12551 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12553 !(Remain & 1); Remain = Remain >> 1)
12554 Offset++;
12555 return Offset;
12556}
12557
12560 // Rotate by getFlagMemberOffset() bits.
12561 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12562 << getFlagMemberOffset());
12563}
12564
12567 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12568 // If the entry is PTR_AND_OBJ but has not been marked with the special
12569 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12570 // marked as MEMBER_OF.
12571 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12573 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12576 return;
12577
12578 // Entries with ATTACH are not members-of anything. They are handled
12579 // separately by the runtime after other maps have been handled.
12580 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12582 return;
12583
12584 // Reset the placeholder value to prepare the flag for the assignment of the
12585 // proper MEMBER_OF value.
12586 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12587 Flags |= MemberOfFlag;
12588}
12589
12593 bool IsDeclaration, bool IsExternallyVisible,
12594 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12595 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12596 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12597 std::function<Constant *()> GlobalInitializer,
12598 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12599 // TODO: convert this to utilise the IRBuilder Config rather than
12600 // a passed down argument.
12601 if (OpenMPSIMD)
12602 return nullptr;
12603
12606 CaptureClause ==
12608 Config.hasRequiresUnifiedSharedMemory())) {
12609 SmallString<64> PtrName;
12610 {
12611 raw_svector_ostream OS(PtrName);
12612 OS << MangledName;
12613 if (!IsExternallyVisible)
12614 OS << format("_%x", EntryInfo.FileID);
12615 OS << "_decl_tgt_ref_ptr";
12616 }
12617
12618 Value *Ptr = M.getNamedValue(PtrName);
12619
12620 if (!Ptr) {
12621 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12622 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12623
12624 auto *GV = cast<GlobalVariable>(Ptr);
12625 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12626
12627 if (!Config.isTargetDevice()) {
12628 if (GlobalInitializer)
12629 GV->setInitializer(GlobalInitializer());
12630 else
12631 GV->setInitializer(GlobalValue);
12632 }
12633
12635 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12636 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12637 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12638 }
12639
12640 return cast<Constant>(Ptr);
12641 }
12642
12643 return nullptr;
12644}
12645
12649 bool IsDeclaration, bool IsExternallyVisible,
12650 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12651 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12652 std::vector<Triple> TargetTriple,
12653 std::function<Constant *()> GlobalInitializer,
12654 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12655 Constant *Addr) {
12657 (TargetTriple.empty() && !Config.isTargetDevice()))
12658 return;
12659
12661 StringRef VarName;
12662 int64_t VarSize;
12664
12666 CaptureClause ==
12668 !Config.hasRequiresUnifiedSharedMemory()) {
12670 VarName = MangledName;
12671 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12672
12673 if (!IsDeclaration)
12674 VarSize = divideCeil(
12675 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12676 else
12677 VarSize = 0;
12678 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12679
12680 // This is a workaround carried over from Clang which prevents undesired
12681 // optimisation of internal variables.
12682 if (Config.isTargetDevice() &&
12683 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12684 // Do not create a "ref-variable" if the original is not also available
12685 // on the host.
12686 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12687 return;
12688
12689 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12690
12691 if (!M.getNamedValue(RefName)) {
12692 Constant *AddrRef =
12693 getOrCreateInternalVariable(Addr->getType(), RefName);
12694 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12695 GvAddrRef->setConstant(true);
12696 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12697 GvAddrRef->setInitializer(Addr);
12698 GeneratedRefs.push_back(GvAddrRef);
12699 }
12700 }
12701 } else {
12704 else
12706
12707 if (Config.isTargetDevice()) {
12708 VarName = (Addr) ? Addr->getName() : "";
12709 Addr = nullptr;
12710 } else {
12712 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12713 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12714 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12715 VarName = (Addr) ? Addr->getName() : "";
12716 }
12717 VarSize = M.getDataLayout().getPointerSize();
12719 }
12720
12721 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12722 Flags, Linkage);
12723}
12724
12725/// Loads all the offload entries information from the host IR
12726/// metadata.
12728 // If we are in target mode, load the metadata from the host IR. This code has
12729 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12730
12731 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12732 if (!MD)
12733 return;
12734
12735 for (MDNode *MN : MD->operands()) {
12736 auto &&GetMDInt = [MN](unsigned Idx) {
12737 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12738 return cast<ConstantInt>(V->getValue())->getZExtValue();
12739 };
12740
12741 auto &&GetMDString = [MN](unsigned Idx) {
12742 auto *V = cast<MDString>(MN->getOperand(Idx));
12743 return V->getString();
12744 };
12745
12746 switch (GetMDInt(0)) {
12747 default:
12748 llvm_unreachable("Unexpected metadata!");
12749 break;
12750 case OffloadEntriesInfoManager::OffloadEntryInfo::
12751 OffloadingEntryInfoTargetRegion: {
12752 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12753 /*DeviceID=*/GetMDInt(1),
12754 /*FileID=*/GetMDInt(2),
12755 /*Line=*/GetMDInt(4),
12756 /*Count=*/GetMDInt(5));
12757 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12758 /*Order=*/GetMDInt(6));
12759 break;
12760 }
12761 case OffloadEntriesInfoManager::OffloadEntryInfo::
12762 OffloadingEntryInfoDeviceGlobalVar:
12763 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12764 /*MangledName=*/GetMDString(1),
12766 /*Flags=*/GetMDInt(2)),
12767 /*Order=*/GetMDInt(3));
12768 break;
12769 }
12770 }
12771}
12772
12774 StringRef HostFilePath) {
12775 if (HostFilePath.empty())
12776 return;
12777
12778 auto Buf = VFS.getBufferForFile(HostFilePath);
12779 if (std::error_code Err = Buf.getError()) {
12780 report_fatal_error(("error opening host file from host file path inside of "
12781 "OpenMPIRBuilder: " +
12782 Err.message())
12783 .c_str());
12784 }
12785
12786 LLVMContext Ctx;
12788 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12789 if (std::error_code Err = M.getError()) {
12791 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12792 .c_str());
12793 }
12794
12795 loadOffloadInfoMetadata(*M.get());
12796}
12797
12800 llvm::StringRef Name) {
12801 Builder.restoreIP(Loc.IP);
12802
12803 BasicBlock *CurBB = Builder.GetInsertBlock();
12804 assert(CurBB &&
12805 "expected a valid insertion block for creating an iterator loop");
12806 Function *F = CurBB->getParent();
12807
12808 InsertPointTy SplitIP = Builder.saveIP();
12809 if (SplitIP.getPoint() == CurBB->end())
12810 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12811 SplitIP = InsertPointTy(CurBB, Terminator->getIterator());
12812
12813 BasicBlock *ContBB =
12814 splitBB(SplitIP, /*CreateBranch=*/false,
12815 Builder.getCurrentDebugLocation(), "omp.it.cont");
12816
12817 CanonicalLoopInfo *CLI =
12818 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12819 /*PreInsertBefore=*/ContBB,
12820 /*PostInsertBefore=*/ContBB, Name);
12821
12822 // Enter loop from original block.
12823 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12824
12825 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12826 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12827 T->eraseFromParent();
12828
12829 InsertPointTy BodyIP = CLI->getBodyIP();
12830 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12831 return Err;
12832
12833 // Body must either fallthrough to the latch or branch directly to it.
12834 if (Instruction *BodyTerminator = CLI->getBody()->getTerminatorOrNull()) {
12835 auto *BodyBr = dyn_cast<UncondBrInst>(BodyTerminator);
12836 if (!BodyBr || BodyBr->getSuccessor() != CLI->getLatch()) {
12838 "iterator bodygen must terminate the canonical body with an "
12839 "unconditional branch to the loop latch",
12841 }
12842 } else {
12843 // Ensure we end the loop body by jumping to the latch.
12844 Builder.SetInsertPoint(CLI->getBody());
12845 Builder.CreateBr(CLI->getLatch());
12846 }
12847
12848 // Link After -> ContBB
12849 Builder.SetInsertPoint(CLI->getAfter(), CLI->getAfter()->begin());
12850 if (!CLI->getAfter()->hasTerminator())
12851 Builder.CreateBr(ContBB);
12852
12853 return InsertPointTy{ContBB, ContBB->begin()};
12854}
12855
12856/// Mangle the parameter part of the vector function name according to
12857/// their OpenMP classification. The mangling function is defined in
12858/// section 4.5 of the AAVFABI(2021Q1).
12859static std::string mangleVectorParameters(
12861 SmallString<256> Buffer;
12862 llvm::raw_svector_ostream Out(Buffer);
12863 for (const auto &ParamAttr : ParamAttrs) {
12864 switch (ParamAttr.Kind) {
12866 Out << 'l';
12867 break;
12869 Out << 'R';
12870 break;
12872 Out << 'U';
12873 break;
12875 Out << 'L';
12876 break;
12878 Out << 'u';
12879 break;
12881 Out << 'v';
12882 break;
12883 }
12884 if (ParamAttr.HasVarStride)
12885 Out << "s" << ParamAttr.StrideOrArg;
12886 else if (ParamAttr.Kind ==
12888 ParamAttr.Kind ==
12890 ParamAttr.Kind ==
12892 ParamAttr.Kind ==
12894 // Don't print the step value if it is not present or if it is
12895 // equal to 1.
12896 if (ParamAttr.StrideOrArg < 0)
12897 Out << 'n' << -ParamAttr.StrideOrArg;
12898 else if (ParamAttr.StrideOrArg != 1)
12899 Out << ParamAttr.StrideOrArg;
12900 }
12901
12902 if (!!ParamAttr.Alignment)
12903 Out << 'a' << ParamAttr.Alignment;
12904 }
12905
12906 return std::string(Out.str());
12907}
12908
12910 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
12912 struct ISADataTy {
12913 char ISA;
12914 unsigned VecRegSize;
12915 };
12916 ISADataTy ISAData[] = {
12917 {'b', 128}, // SSE
12918 {'c', 256}, // AVX
12919 {'d', 256}, // AVX2
12920 {'e', 512}, // AVX512
12921 };
12923 switch (Branch) {
12925 Masked.push_back('N');
12926 Masked.push_back('M');
12927 break;
12929 Masked.push_back('N');
12930 break;
12932 Masked.push_back('M');
12933 break;
12934 }
12935 for (char Mask : Masked) {
12936 for (const ISADataTy &Data : ISAData) {
12938 llvm::raw_svector_ostream Out(Buffer);
12939 Out << "_ZGV" << Data.ISA << Mask;
12940 if (!VLENVal) {
12941 assert(NumElts && "Non-zero simdlen/cdtsize expected");
12942 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
12943 } else {
12944 Out << VLENVal;
12945 }
12946 Out << mangleVectorParameters(ParamAttrs);
12947 Out << '_' << Fn->getName();
12948 Fn->addFnAttr(Out.str());
12949 }
12950 }
12951}
12952
12953// Function used to add the attribute. The parameter `VLEN` is templated to
12954// allow the use of `x` when targeting scalable functions for SVE.
12955template <typename T>
12956static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
12957 char ISA, StringRef ParSeq,
12958 StringRef MangledName, bool OutputBecomesInput,
12959 llvm::Function *Fn) {
12960 SmallString<256> Buffer;
12961 llvm::raw_svector_ostream Out(Buffer);
12962 Out << Prefix << ISA << LMask << VLEN;
12963 if (OutputBecomesInput)
12964 Out << 'v';
12965 Out << ParSeq << '_' << MangledName;
12966 Fn->addFnAttr(Out.str());
12967}
12968
12969// Helper function to generate the Advanced SIMD names depending on the value
12970// of the NDS when simdlen is not present.
12971static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
12972 StringRef Prefix, char ISA,
12973 StringRef ParSeq, StringRef MangledName,
12974 bool OutputBecomesInput,
12975 llvm::Function *Fn) {
12976 switch (NDS) {
12977 case 8:
12978 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12979 OutputBecomesInput, Fn);
12980 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
12981 OutputBecomesInput, Fn);
12982 break;
12983 case 16:
12984 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
12985 OutputBecomesInput, Fn);
12986 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12987 OutputBecomesInput, Fn);
12988 break;
12989 case 32:
12990 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
12991 OutputBecomesInput, Fn);
12992 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
12993 OutputBecomesInput, Fn);
12994 break;
12995 case 64:
12996 case 128:
12997 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
12998 OutputBecomesInput, Fn);
12999 break;
13000 default:
13001 llvm_unreachable("Scalar type is too wide.");
13002 }
13003}
13004
13005/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
13007 llvm::Function *Fn, unsigned UserVLEN,
13009 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
13010 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
13011
13012 // Sort out parameter sequence.
13013 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
13014 StringRef Prefix = "_ZGV";
13015 StringRef MangledName = Fn->getName();
13016
13017 // Generate simdlen from user input (if any).
13018 if (UserVLEN) {
13019 if (ISA == 's') {
13020 // SVE generates only a masked function.
13021 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13022 OutputBecomesInput, Fn);
13023 return;
13024 }
13025
13026 switch (Branch) {
13028 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13029 OutputBecomesInput, Fn);
13030 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13031 OutputBecomesInput, Fn);
13032 break;
13034 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13035 OutputBecomesInput, Fn);
13036 break;
13038 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13039 OutputBecomesInput, Fn);
13040 break;
13041 }
13042 return;
13043 }
13044
13045 if (ISA == 's') {
13046 // SVE, section 3.4.1, item 1.
13047 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13048 OutputBecomesInput, Fn);
13049 return;
13050 }
13051
13052 switch (Branch) {
13054 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13055 MangledName, OutputBecomesInput, Fn);
13056 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13057 MangledName, OutputBecomesInput, Fn);
13058 break;
13060 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13061 MangledName, OutputBecomesInput, Fn);
13062 break;
13064 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13065 MangledName, OutputBecomesInput, Fn);
13066 break;
13067 }
13068}
13069
13070//===----------------------------------------------------------------------===//
13071// OffloadEntriesInfoManager
13072//===----------------------------------------------------------------------===//
13073
13075 return OffloadEntriesTargetRegion.empty() &&
13076 OffloadEntriesDeviceGlobalVar.empty();
13077}
13078
13079unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13080 const TargetRegionEntryInfo &EntryInfo) const {
13081 auto It = OffloadEntriesTargetRegionCount.find(
13082 getTargetRegionEntryCountKey(EntryInfo));
13083 if (It == OffloadEntriesTargetRegionCount.end())
13084 return 0;
13085 return It->second;
13086}
13087
13088void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13089 const TargetRegionEntryInfo &EntryInfo) {
13090 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13091 EntryInfo.Count + 1;
13092}
13093
13094/// Initialize target region entry.
13096 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13097 OffloadEntriesTargetRegion[EntryInfo] =
13098 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13100 ++OffloadingEntriesNum;
13101}
13102
13104 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13106 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13107
13108 // Update the EntryInfo with the next available count for this location.
13109 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13110
13111 // If we are emitting code for a target, the entry is already initialized,
13112 // only has to be registered.
13113 if (OMPBuilder->Config.isTargetDevice()) {
13114 // This could happen if the device compilation is invoked standalone.
13115 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13116 return;
13117 }
13118 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13119 Entry.setAddress(Addr);
13120 Entry.setID(ID);
13121 Entry.setFlags(Flags);
13122 } else {
13124 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13125 return;
13126 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13127 "Target region entry already registered!");
13128 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13129 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13130 ++OffloadingEntriesNum;
13131 }
13132 incrementTargetRegionEntryInfoCount(EntryInfo);
13133}
13134
13136 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13137
13138 // Update the EntryInfo with the next available count for this location.
13139 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13140
13141 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13142 if (It == OffloadEntriesTargetRegion.end()) {
13143 return false;
13144 }
13145 // Fail if this entry is already registered.
13146 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13147 return false;
13148 return true;
13149}
13150
13152 const OffloadTargetRegionEntryInfoActTy &Action) {
13153 // Scan all target region entries and perform the provided action.
13154 for (const auto &It : OffloadEntriesTargetRegion) {
13155 Action(It.first, It.second);
13156 }
13157}
13158
13160 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13161 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13162 ++OffloadingEntriesNum;
13163}
13164
13166 StringRef VarName, Constant *Addr, int64_t VarSize,
13168 if (OMPBuilder->Config.isTargetDevice()) {
13169 // This could happen if the device compilation is invoked standalone.
13170 if (!hasDeviceGlobalVarEntryInfo(VarName))
13171 return;
13172 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13173 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13174 if (Entry.getVarSize() == 0) {
13175 Entry.setVarSize(VarSize);
13176 Entry.setLinkage(Linkage);
13177 }
13178 return;
13179 }
13180 Entry.setVarSize(VarSize);
13181 Entry.setLinkage(Linkage);
13182 Entry.setAddress(Addr);
13183 } else {
13184 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13185 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13186 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13187 "Entry not initialized!");
13188 if (Entry.getVarSize() == 0) {
13189 Entry.setVarSize(VarSize);
13190 Entry.setLinkage(Linkage);
13191 }
13192 return;
13193 }
13195 Flags ==
13197 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13198 Addr, VarSize, Flags, Linkage,
13199 VarName.str());
13200 else
13201 OffloadEntriesDeviceGlobalVar.try_emplace(
13202 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13203 ++OffloadingEntriesNum;
13204 }
13205}
13206
13209 // Scan all target region entries and perform the provided action.
13210 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13211 Action(E.getKey(), E.getValue());
13212}
13213
13214//===----------------------------------------------------------------------===//
13215// CanonicalLoopInfo
13216//===----------------------------------------------------------------------===//
13217
13218void CanonicalLoopInfo::collectControlBlocks(
13220 // We only count those BBs as control block for which we do not need to
13221 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13222 // flow. For consistency, this also means we do not add the Body block, which
13223 // is just the entry to the body code.
13224 BBs.reserve(BBs.size() + 6);
13225 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13226}
13227
13229 assert(isValid() && "Requires a valid canonical loop");
13230 for (BasicBlock *Pred : predecessors(Header)) {
13231 if (Pred != Latch)
13232 return Pred;
13233 }
13234 llvm_unreachable("Missing preheader");
13235}
13236
13237void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13238 assert(isValid() && "Requires a valid canonical loop");
13239
13240 Instruction *CmpI = &getCond()->front();
13241 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13242 CmpI->setOperand(1, TripCount);
13243
13244#ifndef NDEBUG
13245 assertOK();
13246#endif
13247}
13248
13249void CanonicalLoopInfo::mapIndVar(
13250 llvm::function_ref<Value *(Instruction *)> Updater) {
13251 assert(isValid() && "Requires a valid canonical loop");
13252
13253 Instruction *OldIV = getIndVar();
13254
13255 // Record all uses excluding those introduced by the updater. Uses by the
13256 // CanonicalLoopInfo itself to keep track of the number of iterations are
13257 // excluded.
13258 SmallVector<Use *> ReplacableUses;
13259 for (Use &U : OldIV->uses()) {
13260 auto *User = dyn_cast<Instruction>(U.getUser());
13261 if (!User)
13262 continue;
13263 if (User->getParent() == getCond())
13264 continue;
13265 if (User->getParent() == getLatch())
13266 continue;
13267 ReplacableUses.push_back(&U);
13268 }
13269
13270 // Run the updater that may introduce new uses
13271 Value *NewIV = Updater(OldIV);
13272
13273 // Replace the old uses with the value returned by the updater.
13274 for (Use *U : ReplacableUses)
13275 U->set(NewIV);
13276
13277#ifndef NDEBUG
13278 assertOK();
13279#endif
13280}
13281
13283#ifndef NDEBUG
13284 // No constraints if this object currently does not describe a loop.
13285 if (!isValid())
13286 return;
13287
13288 BasicBlock *Preheader = getPreheader();
13289 BasicBlock *Body = getBody();
13290 BasicBlock *After = getAfter();
13291
13292 // Verify standard control-flow we use for OpenMP loops.
13293 assert(Preheader);
13294 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13295 "Preheader must terminate with unconditional branch");
13296 assert(Preheader->getSingleSuccessor() == Header &&
13297 "Preheader must jump to header");
13298
13299 assert(Header);
13300 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13301 "Header must terminate with unconditional branch");
13302 assert(Header->getSingleSuccessor() == Cond &&
13303 "Header must jump to exiting block");
13304
13305 assert(Cond);
13306 assert(Cond->getSinglePredecessor() == Header &&
13307 "Exiting block only reachable from header");
13308
13309 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13310 "Exiting block must terminate with conditional branch");
13311 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13312 "Exiting block's first successor jump to the body");
13313 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13314 "Exiting block's second successor must exit the loop");
13315
13316 assert(Body);
13317 assert(Body->getSinglePredecessor() == Cond &&
13318 "Body only reachable from exiting block");
13319 assert(!isa<PHINode>(Body->front()));
13320
13321 assert(Latch);
13322 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13323 "Latch must terminate with unconditional branch");
13324 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13325 // TODO: To support simple redirecting of the end of the body code that has
13326 // multiple; introduce another auxiliary basic block like preheader and after.
13327 assert(Latch->getSinglePredecessor() != nullptr);
13328 assert(!isa<PHINode>(Latch->front()));
13329
13330 assert(Exit);
13331 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13332 "Exit block must terminate with unconditional branch");
13333 assert(Exit->getSingleSuccessor() == After &&
13334 "Exit block must jump to after block");
13335
13336 assert(After);
13337 assert(After->getSinglePredecessor() == Exit &&
13338 "After block only reachable from exit block");
13339 assert(After->empty() || !isa<PHINode>(After->front()));
13340
13341 Instruction *IndVar = getIndVar();
13342 assert(IndVar && "Canonical induction variable not found?");
13343 assert(isa<IntegerType>(IndVar->getType()) &&
13344 "Induction variable must be an integer");
13345 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13346 "Induction variable must be a PHI in the loop header");
13347 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13348 assert(
13349 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13350 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13351
13352 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13353 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13354 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13355 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13356 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13357 ->isOne());
13358
13359 Value *TripCount = getTripCount();
13360 assert(TripCount && "Loop trip count not found?");
13361 assert(IndVar->getType() == TripCount->getType() &&
13362 "Trip count and induction variable must have the same type");
13363
13364 auto *CmpI = cast<CmpInst>(&Cond->front());
13365 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13366 "Exit condition must be a signed less-than comparison");
13367 assert(CmpI->getOperand(0) == IndVar &&
13368 "Exit condition must compare the induction variable");
13369 assert(CmpI->getOperand(1) == TripCount &&
13370 "Exit condition must compare with the trip count");
13371#endif
13372}
13373
13375 Header = nullptr;
13376 Cond = nullptr;
13377 Latch = nullptr;
13378 Exit = nullptr;
13379}
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),...