LLVM 24.0.0git
OMPIRBuilder.cpp
Go to the documentation of this file.
1//===- OpenMPIRBuilder.cpp - Builder for LLVM-IR for OpenMP directives ----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file
9///
10/// This file implements the OpenMPIRBuilder class, which is used as a
11/// convenient way to create LLVM instructions for OpenMP directives.
12///
13//===----------------------------------------------------------------------===//
14
17#include "llvm/ADT/SmallSet.h"
20#include "llvm/ADT/StringRef.h"
31#include "llvm/IR/Attributes.h"
32#include "llvm/IR/BasicBlock.h"
33#include "llvm/IR/CFG.h"
34#include "llvm/IR/CallingConv.h"
35#include "llvm/IR/Constant.h"
36#include "llvm/IR/Constants.h"
37#include "llvm/IR/DIBuilder.h"
40#include "llvm/IR/Function.h"
42#include "llvm/IR/IRBuilder.h"
45#include "llvm/IR/LLVMContext.h"
46#include "llvm/IR/MDBuilder.h"
47#include "llvm/IR/Metadata.h"
49#include "llvm/IR/PassManager.h"
51#include "llvm/IR/Value.h"
54#include "llvm/Support/Error.h"
66
67#include <cstdint>
68#include <optional>
69
70#define DEBUG_TYPE "openmp-ir-builder"
71
72using namespace llvm;
73using namespace omp;
74
75static cl::opt<bool>
76 OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden,
77 cl::desc("Use optimistic attributes describing "
78 "'as-if' properties of runtime calls."),
79 cl::init(false));
80
82 "openmp-ir-builder-unroll-threshold-factor", cl::Hidden,
83 cl::desc("Factor for the unroll threshold to account for code "
84 "simplifications still taking place"),
85 cl::init(1.5));
86
88 "openmp-ir-builder-use-default-max-threads", cl::Hidden,
89 cl::desc("Use a default max threads if none is provided."), cl::init(true));
90
91#ifndef NDEBUG
92/// Return whether IP1 and IP2 are ambiguous, i.e. that inserting instructions
93/// at position IP1 may change the meaning of IP2 or vice-versa. This is because
94/// an InsertPoint stores the instruction before something is inserted. For
95/// instance, if both point to the same instruction, two IRBuilders alternating
96/// creating instruction will cause the instructions to be interleaved.
99 if (!IP1.isSet() || !IP2.isSet())
100 return false;
101 return IP1.getBlock() == IP2.getBlock() && IP1.getPoint() == IP2.getPoint();
102}
103
105 // Valid ordered/unordered and base algorithm combinations.
106 switch (SchedType & ~OMPScheduleType::MonotonicityMask) {
107 case OMPScheduleType::UnorderedStaticChunked:
108 case OMPScheduleType::UnorderedStatic:
109 case OMPScheduleType::UnorderedDynamicChunked:
110 case OMPScheduleType::UnorderedGuidedChunked:
111 case OMPScheduleType::UnorderedRuntime:
112 case OMPScheduleType::UnorderedAuto:
113 case OMPScheduleType::UnorderedTrapezoidal:
114 case OMPScheduleType::UnorderedGreedy:
115 case OMPScheduleType::UnorderedBalanced:
116 case OMPScheduleType::UnorderedGuidedIterativeChunked:
117 case OMPScheduleType::UnorderedGuidedAnalyticalChunked:
118 case OMPScheduleType::UnorderedSteal:
119 case OMPScheduleType::UnorderedStaticBalancedChunked:
120 case OMPScheduleType::UnorderedGuidedSimd:
121 case OMPScheduleType::UnorderedRuntimeSimd:
122 case OMPScheduleType::OrderedStaticChunked:
123 case OMPScheduleType::OrderedStatic:
124 case OMPScheduleType::OrderedDynamicChunked:
125 case OMPScheduleType::OrderedGuidedChunked:
126 case OMPScheduleType::OrderedRuntime:
127 case OMPScheduleType::OrderedAuto:
128 case OMPScheduleType::OrderdTrapezoidal:
129 case OMPScheduleType::NomergeUnorderedStaticChunked:
130 case OMPScheduleType::NomergeUnorderedStatic:
131 case OMPScheduleType::NomergeUnorderedDynamicChunked:
132 case OMPScheduleType::NomergeUnorderedGuidedChunked:
133 case OMPScheduleType::NomergeUnorderedRuntime:
134 case OMPScheduleType::NomergeUnorderedAuto:
135 case OMPScheduleType::NomergeUnorderedTrapezoidal:
136 case OMPScheduleType::NomergeUnorderedGreedy:
137 case OMPScheduleType::NomergeUnorderedBalanced:
138 case OMPScheduleType::NomergeUnorderedGuidedIterativeChunked:
139 case OMPScheduleType::NomergeUnorderedGuidedAnalyticalChunked:
140 case OMPScheduleType::NomergeUnorderedSteal:
141 case OMPScheduleType::NomergeOrderedStaticChunked:
142 case OMPScheduleType::NomergeOrderedStatic:
143 case OMPScheduleType::NomergeOrderedDynamicChunked:
144 case OMPScheduleType::NomergeOrderedGuidedChunked:
145 case OMPScheduleType::NomergeOrderedRuntime:
146 case OMPScheduleType::NomergeOrderedAuto:
147 case OMPScheduleType::NomergeOrderedTrapezoidal:
148 case OMPScheduleType::OrderedDistributeChunked:
149 case OMPScheduleType::OrderedDistribute:
150 break;
151 default:
152 return false;
153 }
154
155 // Must not set both monotonicity modifiers at the same time.
156 OMPScheduleType MonotonicityFlags =
157 SchedType & OMPScheduleType::MonotonicityMask;
158 if (MonotonicityFlags == OMPScheduleType::MonotonicityMask)
159 return false;
160
161 return true;
162}
163#endif
164
165/// This is a wrapper over IRBuilderBase::restoreIP that also restores a current
166/// debug location when the insert point is at the end of a block. It picks a
167/// location scoped to the current function: the block's last instruction
168/// location if the block is non-empty, otherwise a location synthesized from
169/// the function's subprogram (when the function has debug info).
172 Builder.restoreIP(IP);
173 // When IP points at a real instruction, restoreIP (SetInsertPoint) already
174 // set the debug location from that instruction, so leave it alone.
175 llvm::BasicBlock *BB = Builder.GetInsertBlock();
176 if (Builder.GetInsertPoint() != BB->end())
177 return;
178
179 // At the end of a block, pick a location guaranteed to belong to the current
180 // insertion function's subprogram. Prefer the block's own last instruction;
181 // otherwise synthesize a location from the function's subprogram.
182 if (!BB->empty())
183 Builder.SetCurrentDebugLocation(BB->back().getStableDebugLoc());
184 else if (llvm::DISubprogram *FSP =
185 BB->getParent() ? BB->getParent()->getSubprogram() : nullptr) {
186 unsigned Line = FSP->getScopeLine() ? FSP->getScopeLine() : FSP->getLine();
187 Builder.SetCurrentDebugLocation(
188 llvm::DILocation::get(FSP->getContext(), Line, /*Column=*/0, FSP));
189 }
190}
191
192static bool hasGridValue(const Triple &T) {
193 return T.isAMDGPU() || T.isNVPTX() || T.isSPIRV();
194}
195
196static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {
197 if (T.isAMDGPU()) {
198 StringRef Features =
199 Kernel->getFnAttribute("target-features").getValueAsString();
200 if (Features.count("+wavefrontsize64"))
203 }
204 if (T.isNVPTX())
206 if (T.isSPIRV())
208 llvm_unreachable("No grid value available for this architecture!");
209}
210
211/// Determine which scheduling algorithm to use, determined from schedule clause
212/// arguments.
213static OMPScheduleType
214getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,
215 bool HasSimdModifier, bool HasDistScheduleChunks) {
216 // Currently, the default schedule it static.
217 switch (ClauseKind) {
218 case OMP_SCHEDULE_Default:
219 case OMP_SCHEDULE_Static:
220 return HasChunks ? OMPScheduleType::BaseStaticChunked
221 : OMPScheduleType::BaseStatic;
222 case OMP_SCHEDULE_Dynamic:
223 return OMPScheduleType::BaseDynamicChunked;
224 case OMP_SCHEDULE_Guided:
225 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd
226 : OMPScheduleType::BaseGuidedChunked;
227 case OMP_SCHEDULE_Auto:
229 case OMP_SCHEDULE_Runtime:
230 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd
231 : OMPScheduleType::BaseRuntime;
232 case OMP_SCHEDULE_Distribute:
233 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked
234 : OMPScheduleType::BaseDistribute;
235 }
236 llvm_unreachable("unhandled schedule clause argument");
237}
238
239/// Adds ordering modifier flags to schedule type.
240static OMPScheduleType
242 bool HasOrderedClause) {
243 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==
244 OMPScheduleType::None &&
245 "Must not have ordering nor monotonicity flags already set");
246
247 OMPScheduleType OrderingModifier = HasOrderedClause
248 ? OMPScheduleType::ModifierOrdered
249 : OMPScheduleType::ModifierUnordered;
250 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;
251
252 // Unsupported combinations
253 if (OrderingScheduleType ==
254 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))
255 return OMPScheduleType::OrderedGuidedChunked;
256 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |
257 OMPScheduleType::ModifierOrdered))
258 return OMPScheduleType::OrderedRuntime;
259
260 return OrderingScheduleType;
261}
262
263/// Adds monotonicity modifier flags to schedule type.
264static OMPScheduleType
266 bool HasSimdModifier, bool HasMonotonic,
267 bool HasNonmonotonic, bool HasOrderedClause) {
268 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==
269 OMPScheduleType::None &&
270 "Must not have monotonicity flags already set");
271 assert((!HasMonotonic || !HasNonmonotonic) &&
272 "Monotonic and Nonmonotonic are contradicting each other");
273
274 if (HasMonotonic) {
275 return ScheduleType | OMPScheduleType::ModifierMonotonic;
276 } else if (HasNonmonotonic) {
277 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
278 } else {
279 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.
280 // If the static schedule kind is specified or if the ordered clause is
281 // specified, and if the nonmonotonic modifier is not specified, the
282 // effect is as if the monotonic modifier is specified. Otherwise, unless
283 // the monotonic modifier is specified, the effect is as if the
284 // nonmonotonic modifier is specified.
285 OMPScheduleType BaseScheduleType =
286 ScheduleType & ~OMPScheduleType::ModifierMask;
287 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||
288 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||
289 HasOrderedClause) {
290 // The monotonic is used by default in openmp runtime library, so no need
291 // to set it.
292 return ScheduleType;
293 } else {
294 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
295 }
296 }
297}
298
299/// Determine the schedule type using schedule and ordering clause arguments.
300static OMPScheduleType
301computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,
302 bool HasSimdModifier, bool HasMonotonicModifier,
303 bool HasNonmonotonicModifier, bool HasOrderedClause,
304 bool HasDistScheduleChunks) {
306 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);
307 OMPScheduleType OrderedSchedule =
308 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);
310 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,
311 HasNonmonotonicModifier, HasOrderedClause);
312
314 return Result;
315}
316
317/// Given a function, if it represents the entry point of a target kernel, this
318/// returns the execution mode flags associated with that kernel.
319static std::optional<omp::OMPTgtExecModeFlags>
321 CallInst *TargetInitCall = nullptr;
322 for (Instruction &Inst : Kernel.getEntryBlock()) {
323 if (auto *Call = dyn_cast<CallInst>(&Inst)) {
324 if (Call->getCalledFunction()->getName() == "__kmpc_target_init") {
325 TargetInitCall = Call;
326 break;
327 }
328 }
329 }
330
331 if (!TargetInitCall)
332 return std::nullopt;
333
334 // Get the kernel mode information from the global variable associated to the
335 // first argument to the call to __kmpc_target_init. Refer to
336 // createTargetInit() to see how this is initialized.
337 Value *InitOperand = TargetInitCall->getArgOperand(0);
338 GlobalVariable *KernelEnv = nullptr;
339 if (auto *Cast = dyn_cast<ConstantExpr>(InitOperand))
340 KernelEnv = cast<GlobalVariable>(Cast->getOperand(0));
341 else
342 KernelEnv = cast<GlobalVariable>(InitOperand);
343 auto *KernelEnvInit = cast<ConstantStruct>(KernelEnv->getInitializer());
344 auto *ConfigEnv = cast<ConstantStruct>(KernelEnvInit->getOperand(0));
345 auto *KernelMode = cast<ConstantInt>(ConfigEnv->getOperand(2));
346 return static_cast<OMPTgtExecModeFlags>(KernelMode->getZExtValue());
347}
348
349static bool isGenericKernel(Function &Fn) {
350 std::optional<omp::OMPTgtExecModeFlags> ExecMode =
352 return !ExecMode || (*ExecMode & OMP_TGT_EXEC_MODE_GENERIC);
353}
354
355/// Make \p Source branch to \p Target.
356///
357/// Handles two situations:
358/// * \p Source already has an unconditional branch.
359/// * \p Source is a degenerate block (no terminator because the BB is
360/// the current head of the IR construction).
362 if (Instruction *Term = Source->getTerminatorOrNull()) {
363 auto *Br = cast<UncondBrInst>(Term);
364 BasicBlock *Succ = Br->getSuccessor();
365 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);
366 Br->setSuccessor(Target);
367 return;
368 }
369
370 auto *NewBr = UncondBrInst::Create(Target, Source);
371 NewBr->setDebugLoc(DL);
372}
373
375 bool CreateBranch, DebugLoc DL) {
376 assert(New->getFirstInsertionPt() == New->begin() &&
377 "Target BB must not have PHI nodes");
378
379 // Move instructions to new block.
380 BasicBlock *Old = IP.getBlock();
381 // If the `Old` block is empty then there are no instructions to move. But in
382 // the new debug scheme, it could have trailing debug records which will be
383 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2
384 // reasons:
385 // 1. If `New` is also empty, `BasicBlock::splice` crashes.
386 // 2. Even if `New` is not empty, the rationale to move those records to `New`
387 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function
388 // assumes that `Old` is optimized out and is going away. This is not the case
389 // here. The `Old` block is still being used e.g. a branch instruction is
390 // added to it later in this function.
391 // So we call `BasicBlock::splice` only when `Old` is not empty.
392 if (!Old->empty())
393 New->splice(New->begin(), Old, IP.getPoint(), Old->end());
394
395 if (CreateBranch) {
396 auto *NewBr = UncondBrInst::Create(New, Old);
397 NewBr->setDebugLoc(DL);
398 }
399}
400
401void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {
402 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
403 BasicBlock *Old = Builder.GetInsertBlock();
404
405 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);
406 if (CreateBranch)
407 Builder.SetInsertPoint(Old->getTerminator());
408 else
409 Builder.SetInsertPoint(Old);
410
411 // SetInsertPoint also updates the Builder's debug location, but we want to
412 // keep the one the Builder was configured to use.
413 Builder.SetCurrentDebugLocation(DebugLoc);
414}
415
417 DebugLoc DL, llvm::Twine Name) {
418 BasicBlock *Old = IP.getBlock();
420 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,
421 Old->getParent(), Old->getNextNode());
422 spliceBB(IP, New, CreateBranch, DL);
423 New->replaceSuccessorsPhiUsesWith(Old, New);
424 return New;
425}
426
427BasicBlock *llvm::splitBB(IRBuilderBase &Builder, bool CreateBranch,
428 llvm::Twine Name) {
429 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
430 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
431 if (CreateBranch)
432 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
433 else
434 Builder.SetInsertPoint(Builder.GetInsertBlock());
435 // SetInsertPoint also updates the Builder's debug location, but we want to
436 // keep the one the Builder was configured to use.
437 Builder.SetCurrentDebugLocation(DebugLoc);
438 return New;
439}
440
441BasicBlock *llvm::splitBB(IRBuilder<> &Builder, bool CreateBranch,
442 llvm::Twine Name) {
443 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
444 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
445 if (CreateBranch)
446 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
447 else
448 Builder.SetInsertPoint(Builder.GetInsertBlock());
449 // SetInsertPoint also updates the Builder's debug location, but we want to
450 // keep the one the Builder was configured to use.
451 Builder.SetCurrentDebugLocation(DebugLoc);
452 return New;
453}
454
456 llvm::Twine Suffix) {
457 BasicBlock *Old = Builder.GetInsertBlock();
458 return splitBB(Builder, CreateBranch, Old->getName() + Suffix);
459}
460
461// This function creates a fake integer value and a fake use for the integer
462// value. It returns the fake value created. This is useful in modeling the
463// extra arguments to the outlined functions.
465 OpenMPIRBuilder::InsertPointTy OuterAllocaIP,
467 OpenMPIRBuilder::InsertPointTy InnerAllocaIP,
468 const Twine &Name = "", bool AsPtr = true,
469 bool Is64Bit = false) {
470 Builder.restoreIP(OuterAllocaIP);
471 IntegerType *IntTy = Is64Bit ? Builder.getInt64Ty() : Builder.getInt32Ty();
472 Instruction *FakeVal;
473 AllocaInst *FakeValAddr =
474 Builder.CreateAlloca(IntTy, nullptr, Name + ".addr");
475 ToBeDeleted.push_back(FakeValAddr);
476
477 if (AsPtr) {
478 FakeVal = FakeValAddr;
479 } else {
480 FakeVal = Builder.CreateLoad(IntTy, FakeValAddr, Name + ".val");
481 ToBeDeleted.push_back(FakeVal);
482 }
483
484 // Generate a fake use of this value
485 Builder.restoreIP(InnerAllocaIP);
486 Instruction *UseFakeVal;
487 if (AsPtr) {
488 UseFakeVal = Builder.CreateLoad(IntTy, FakeVal, Name + ".use");
489 } else {
490 UseFakeVal = cast<BinaryOperator>(Builder.CreateAdd(
491 FakeVal, Is64Bit ? Builder.getInt64(10) : Builder.getInt32(10)));
492 }
493 ToBeDeleted.push_back(UseFakeVal);
494 return FakeVal;
495}
496
497//===----------------------------------------------------------------------===//
498// OpenMPIRBuilderConfig
499//===----------------------------------------------------------------------===//
500
501namespace {
503/// Values for bit flags for marking which requires clauses have been used.
504enum OpenMPOffloadingRequiresDirFlags {
505 /// flag undefined.
506 OMP_REQ_UNDEFINED = 0x000,
507 /// no requires directive present.
508 OMP_REQ_NONE = 0x001,
509 /// reverse_offload clause.
510 OMP_REQ_REVERSE_OFFLOAD = 0x002,
511 /// unified_address clause.
512 OMP_REQ_UNIFIED_ADDRESS = 0x004,
513 /// unified_shared_memory clause.
514 OMP_REQ_UNIFIED_SHARED_MEMORY = 0x008,
515 /// dynamic_allocators clause.
516 OMP_REQ_DYNAMIC_ALLOCATORS = 0x010,
517 LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)
518};
519
520class OMPCodeExtractor : public CodeExtractor {
521public:
522 OMPCodeExtractor(OpenMPIRBuilder &OMPBuilder, ArrayRef<BasicBlock *> BBs,
523 DominatorTree *DT = nullptr, bool AggregateArgs = false,
524 BlockFrequencyInfo *BFI = nullptr,
525 BranchProbabilityInfo *BPI = nullptr,
526 AssumptionCache *AC = nullptr, bool AllowVarArgs = false,
527 bool AllowAlloca = false,
528 BasicBlock *AllocationBlock = nullptr,
529 ArrayRef<BasicBlock *> DeallocationBlocks = {},
530 std::string Suffix = "", bool ArgsInZeroAddressSpace = false)
531 : CodeExtractor(BBs, DT, AggregateArgs, BFI, BPI, AC, AllowVarArgs,
532 AllowAlloca, AllocationBlock, DeallocationBlocks, Suffix,
533 ArgsInZeroAddressSpace),
534 OMPBuilder(OMPBuilder) {}
535
536 virtual ~OMPCodeExtractor() = default;
537
538protected:
539 OpenMPIRBuilder &OMPBuilder;
540};
541
542class DeviceSharedMemCodeExtractor : public OMPCodeExtractor {
543public:
544 using OMPCodeExtractor::OMPCodeExtractor;
545 virtual ~DeviceSharedMemCodeExtractor() = default;
546
547protected:
548 virtual Instruction *
549 allocateVar(IRBuilder<>::InsertPoint AllocaIP, Type *VarType,
550 const Twine &Name = Twine(""),
551 AddrSpaceCastInst **CastedAlloc = nullptr) override {
552 return OMPBuilder.createOMPAllocShared(AllocaIP, VarType, Name);
553 }
554
555 virtual Instruction *deallocateVar(IRBuilder<>::InsertPoint DeallocIP,
556 Value *Var, Type *VarType) override {
557 return OMPBuilder.createOMPFreeShared(DeallocIP, Var, VarType);
558 }
559};
560
561/// Helper storing information about regions to outline using device shared
562/// memory for intermediate allocations.
563struct DeviceSharedMemOutlineInfo : public OpenMPIRBuilder::OutlineInfo {
564 OpenMPIRBuilder &OMPBuilder;
565
566 DeviceSharedMemOutlineInfo(OpenMPIRBuilder &OMPBuilder)
567 : OMPBuilder(OMPBuilder) {}
568 virtual ~DeviceSharedMemOutlineInfo() = default;
569
570 virtual std::unique_ptr<CodeExtractor>
571 createCodeExtractor(ArrayRef<BasicBlock *> Blocks,
572 bool ArgsInZeroAddressSpace,
573 Twine Suffix = Twine("")) override;
574};
575
576} // anonymous namespace
577
579 : RequiresFlags(OMP_REQ_UNDEFINED) {}
580
583 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,
584 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)
587 RequiresFlags(OMP_REQ_UNDEFINED) {
588 if (HasRequiresReverseOffload)
589 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
590 if (HasRequiresUnifiedAddress)
591 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
592 if (HasRequiresUnifiedSharedMemory)
593 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
594 if (HasRequiresDynamicAllocators)
595 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
596}
597
599 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;
600}
601
603 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;
604}
605
607 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;
608}
609
611 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;
612}
613
615 return hasRequiresFlags() ? RequiresFlags
616 : static_cast<int64_t>(OMP_REQ_NONE);
617}
618
620 if (Value)
621 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
622 else
623 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;
624}
625
627 if (Value)
628 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
629 else
630 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;
631}
632
634 if (Value)
635 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
636 else
637 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;
638}
639
641 if (Value)
642 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
643 else
644 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;
645}
646
647//===----------------------------------------------------------------------===//
648// OpenMPIRBuilder
649//===----------------------------------------------------------------------===//
650
653 SmallVector<Value *> &ArgsVector) {
655 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);
656 auto Int32Ty = Type::getInt32Ty(Builder.getContext());
657 constexpr size_t MaxDim = 3;
658 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));
659
660 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);
661
662 Value *DynCGroupMemFallbackFlag =
663 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));
664 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);
665
666 Value *StrictFlag = Builder.getInt64(KernelArgs.StrictBlocksAndThreads);
667 StrictFlag = Builder.CreateShl(StrictFlag, 6);
668
669 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
670 Flags = Builder.CreateOr(Flags, StrictFlag);
671
672 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
673
674 Value *NumTeams3D =
675 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
676 Value *NumThreads3D =
677 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
678 for (unsigned I :
679 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
680 NumTeams3D =
681 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
682 for (unsigned I :
683 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
684 NumThreads3D =
685 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
686
687 ArgsVector = {Version,
688 PointerNum,
689 KernelArgs.RTArgs.BasePointersArray,
690 KernelArgs.RTArgs.PointersArray,
691 KernelArgs.RTArgs.SizesArray,
692 KernelArgs.RTArgs.MapTypesArray,
693 KernelArgs.RTArgs.MapNamesArray,
694 KernelArgs.RTArgs.MappersArray,
695 KernelArgs.NumIterations,
696 Flags,
697 NumTeams3D,
698 NumThreads3D,
699 KernelArgs.DynCGroupMem};
700}
701
703 LLVMContext &Ctx = Fn.getContext();
704
705 // Get the function's current attributes.
706 auto Attrs = Fn.getAttributes();
707 auto FnAttrs = Attrs.getFnAttrs();
708 auto RetAttrs = Attrs.getRetAttrs();
710 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
711 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
712
713 // Add AS to FnAS while taking special care with integer extensions.
714 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
715 bool Param = true) -> void {
716 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
717 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
718 if (HasSignExt || HasZeroExt) {
719 assert(AS.getNumAttributes() == 1 &&
720 "Currently not handling extension attr combined with others.");
721 if (Param) {
722 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
723 FnAS = FnAS.addAttribute(Ctx, AK);
724 } else if (auto AK =
725 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
726 FnAS = FnAS.addAttribute(Ctx, AK);
727 } else {
728 FnAS = FnAS.addAttributes(Ctx, AS);
729 }
730 };
731
732#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
733#include "llvm/Frontend/OpenMP/OMPKinds.def"
734
735 // Add attributes to the function declaration.
736 switch (FnID) {
737#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
738 case Enum: \
739 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
740 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
741 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
742 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
743 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
744 break;
745#include "llvm/Frontend/OpenMP/OMPKinds.def"
746 default:
747 // Attributes are optional.
748 break;
749 }
750}
751
754 FunctionType *FnTy = nullptr;
755 Function *Fn = nullptr;
756
757 // Try to find the declation in the module first.
758 switch (FnID) {
759#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
760 case Enum: \
761 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
762 IsVarArg); \
763 Fn = M.getFunction(Str); \
764 break;
765#include "llvm/Frontend/OpenMP/OMPKinds.def"
766 }
767
768 if (!Fn) {
769 // Create a new declaration if we need one.
770 switch (FnID) {
771#define OMP_RTL(Enum, Str, ...) \
772 case Enum: \
773 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
774 break;
775#include "llvm/Frontend/OpenMP/OMPKinds.def"
776 }
777 Fn->setCallingConv(Config.getRuntimeCC());
778 // Add information if the runtime function takes a callback function
779 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
780 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
781 LLVMContext &Ctx = Fn->getContext();
782 MDBuilder MDB(Ctx);
783 // Annotate the callback behavior of the runtime function:
784 // - The callback callee is argument number 2 (microtask).
785 // - The first two arguments of the callback callee are unknown (-1).
786 // - All variadic arguments to the runtime function are passed to the
787 // callback callee.
788 Fn->addMetadata(
789 LLVMContext::MD_callback,
791 2, {-1, -1}, /* VarArgsArePassed */ true)}));
792 }
793 }
794
795 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
796 << " with type " << *Fn->getFunctionType() << "\n");
797 addAttributes(FnID, *Fn);
798
799 } else {
800 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
801 << " with type " << *Fn->getFunctionType() << "\n");
802 }
803
804 assert(Fn && "Failed to create OpenMP runtime function");
805
806 return {FnTy, Fn};
807}
808
811 if (!FiniBB) {
812 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
814 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
815 Builder.SetInsertPoint(FiniBB);
816 // FiniCB adds the branch to the exit stub.
817 if (Error Err = FiniCB(Builder.saveIP()))
818 return Err;
819 }
820 return FiniBB;
821}
822
824 BasicBlock *OtherFiniBB) {
825 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
826 if (!FiniBB) {
827 FiniBB = OtherFiniBB;
828
829 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
830 if (Error Err = FiniCB(Builder.saveIP()))
831 return Err;
832
833 return Error::success();
834 }
835
836 // Move instructions from FiniBB to the start of OtherFiniBB.
837 auto EndIt = FiniBB->end();
838 if (FiniBB->size() >= 1)
839 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
840 EndIt = Prev;
841 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
842 EndIt);
843
844 FiniBB->replaceAllUsesWith(OtherFiniBB);
845 FiniBB->eraseFromParent();
846 FiniBB = OtherFiniBB;
847 return Error::success();
848}
849
852 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
853 assert(Fn && "Failed to create OpenMP runtime function pointer");
854 return Fn;
855}
856
859 StringRef Name) {
860 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
861 Call->setCallingConv(Config.getRuntimeCC());
862 return Call;
863}
864
865void OpenMPIRBuilder::initialize() { initializeTypes(M); }
866
869 BasicBlock &EntryBlock = Function->getEntryBlock();
870 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
871
872 // Loop over blocks looking for constant allocas, skipping the entry block
873 // as any allocas there are already in the desired location.
874 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
875 Block++) {
876 for (auto Inst = Block->getReverseIterator()->begin();
877 Inst != Block->getReverseIterator()->end();) {
879 Inst++;
881 continue;
882 AllocaInst->moveBeforePreserving(MoveLocInst);
883 } else {
884 Inst++;
885 }
886 }
887 }
888}
889
892
893 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
894 // TODO: For now, we support simple static allocations, we might need to
895 // move non-static ones as well. However, this will need further analysis to
896 // move the lenght arguments as well.
898 };
899
900 for (llvm::Instruction &Inst : Block)
902 if (ShouldHoistAlloca(*AllocaInst))
903 AllocasToMove.push_back(AllocaInst);
904
905 auto InsertPoint =
906 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
907
908 for (llvm::Instruction *AllocaInst : AllocasToMove)
910}
911
913 PostDominatorTree PostDomTree(*Func);
914 for (llvm::BasicBlock &BB : *Func)
915 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
917}
918
920 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
922 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
923 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
924 // Skip functions that have not finalized yet; may happen with nested
925 // function generation.
926 if (Fn && OI->getFunction() != Fn) {
927 DeferredOutlines.push_back(std::move(OI));
928 continue;
929 }
930
931 ParallelRegionBlockSet.clear();
932 Blocks.clear();
933 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
934
935 Function *OuterFn = OI->getFunction();
936 CodeExtractorAnalysisCache CEAC(*OuterFn);
937 // If we generate code for the target device, we need to allocate
938 // struct for aggregate params in the device default alloca address space.
939 // OpenMP runtime requires that the params of the extracted functions are
940 // passed as zero address space pointers. This flag ensures that
941 // CodeExtractor generates correct code for extracted functions
942 // which are used by OpenMP runtime.
943 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
944 std::unique_ptr<CodeExtractor> Extractor =
945 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
946
947 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
948 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
949 << " Exit: " << OI->ExitBB->getName() << "\n");
950 assert(Extractor->isEligible() &&
951 "Expected OpenMP outlining to be possible!");
952
953 for (auto *V : OI->ExcludeArgsFromAggregate)
954 Extractor->excludeArgFromAggregate(V);
955
956 Function *OutlinedFn =
957 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
958
959 // Forward target-cpu, target-features attributes to the outlined function.
960 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
961 if (TargetCpuAttr.isStringAttribute())
962 OutlinedFn->addFnAttr(TargetCpuAttr);
963
964 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
965 if (TargetFeaturesAttr.isStringAttribute())
966 OutlinedFn->addFnAttr(TargetFeaturesAttr);
967
968 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
969 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
970 assert(OutlinedFn->getReturnType()->isVoidTy() &&
971 "OpenMP outlined functions should not return a value!");
972
973 // For compability with the clang CG we move the outlined function after the
974 // one with the parallel region.
975 OutlinedFn->removeFromParent();
976 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
977
978 // Remove the artificial entry introduced by the extractor right away, we
979 // made our own entry block after all.
980 {
981 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
982 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
983 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
984 // Move instructions from the to-be-deleted ArtificialEntry to the entry
985 // basic block of the parallel region. CodeExtractor generates
986 // instructions to unwrap the aggregate argument and may sink
987 // allocas/bitcasts for values that are solely used in the outlined region
988 // and do not escape.
989 assert(!ArtificialEntry.empty() &&
990 "Expected instructions to add in the outlined region entry");
991 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
992 End = ArtificialEntry.rend();
993 It != End;) {
994 Instruction &I = *It;
995 It++;
996
997 if (I.isTerminator()) {
998 // Absorb any debug value that terminator may have
999 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
1000 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
1001 continue;
1002 }
1003
1004 I.moveBeforePreserving(*OI->EntryBB,
1005 OI->EntryBB->getFirstInsertionPt());
1006 }
1007
1008 OI->EntryBB->moveBefore(&ArtificialEntry);
1009 ArtificialEntry.eraseFromParent();
1010 }
1011 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
1012 assert(OutlinedFn && OutlinedFn->hasNUses(1));
1013
1014 // Run a user callback, e.g. to add attributes.
1015 if (OI->PostOutlineCB)
1016 OI->PostOutlineCB(*OutlinedFn);
1017
1018 if (OI->FixUpNonEntryAllocas)
1020 }
1021
1022 // Remove work items that have been completed.
1023 OutlineInfos = std::move(DeferredOutlines);
1024
1025 // The createTarget functions embeds user written code into
1026 // the target region which may inject allocas which need to
1027 // be moved to the entry block of our target or risk malformed
1028 // optimisations by later passes, this is only relevant for
1029 // the device pass which appears to be a little more delicate
1030 // when it comes to optimisations (however, we do not block on
1031 // that here, it's up to the inserter to the list to do so).
1032 // This notbaly has to occur after the OutlinedInfo candidates
1033 // have been extracted so we have an end product that will not
1034 // be implicitly adversely affected by any raises unless
1035 // intentionally appended to the list.
1036 // NOTE: This only does so for ConstantData, it could be extended
1037 // to ConstantExpr's with further effort, however, they should
1038 // largely be folded when they get here. Extending it to runtime
1039 // defined/read+writeable allocation sizes would be non-trivial
1040 // (need to factor in movement of any stores to variables the
1041 // allocation size depends on, as well as the usual loads,
1042 // otherwise it'll yield the wrong result after movement) and
1043 // likely be more suitable as an LLVM optimisation pass.
1046
1047 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1048 [](EmitMetadataErrorKind Kind,
1049 const TargetRegionEntryInfo &EntryInfo) -> void {
1050 errs() << "Error of kind: " << Kind
1051 << " when emitting offload entries and metadata during "
1052 "OMPIRBuilder finalization \n";
1053 };
1054
1055 if (!OffloadInfoManager.empty())
1057
1058 // Rewrite uses of globals to their replacement declare target globals if
1059 // we are processing a device module.
1060 if (Config.isTargetDevice())
1061 applyDeclareTargetGlobalReplacements();
1062
1063 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1064 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1065 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1066 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1067 }
1068
1069 IsFinalized = true;
1070}
1071
1072bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1073
1075 GlobalValue *Original, GlobalValue *Replacement) {
1076 assert(Original && Replacement &&
1077 "Null values provided to registerDeclareTargetGlobalReplacement");
1078 DeclareTargetGlobalReplacements.push_back({Original, Replacement});
1079}
1080
1081void OpenMPIRBuilder::applyDeclareTargetGlobalReplacements() {
1082 for (DeclareTargetGlobalReplacement &R : DeclareTargetGlobalReplacements) {
1083 GlobalValue *OldGV = R.Original;
1084 GlobalValue *NewGV = R.Replacement;
1085
1086 assert(OldGV && NewGV &&
1087 "A null value was inserted into DeclareTargetGlobalReplacements");
1088
1089 // The assert above should catch this case, but this is kept to attempt
1090 // to proceed without issue when asserts are off.
1091 if (!OldGV || !NewGV)
1092 continue;
1093
1094 // The replacement global is a reference pointer that holds the
1095 // address of the device-resident storage. Every use must load the
1096 // reference pointer first and use the loaded address.
1097 //
1098 // Constant expression users (e.g. a constant GEP embedded in another
1099 // global's initializer or in an instruction) cannot have a load inserted
1100 // in place, so first expand any constant-expression users that live inside
1101 // functions into instructions. Any remaining constant users are handled
1102 // via a direct constant rewrite below as we cannot materialize a load
1103 // there.
1104 //
1105 // NOTE: We extend the constant rewrite to module scope, as we replace all
1106 // usages.
1107 if (auto *OldConst = dyn_cast<Constant>(OldGV))
1109 /*RestrictToFunc=*/nullptr,
1110 /*RemoveDeadConstants=*/false);
1111
1112 IRBuilderBase::InsertPointGuard Guard(Builder);
1114 for (User *U : Users) {
1115 auto *Insn = dyn_cast<Instruction>(U);
1116 if (!Insn)
1117 continue;
1118
1119 // A PHI node cannot have a load inserted immediately before it, as PHIs
1120 // must remain grouped at the top of their basic block. So we need to
1121 // make sure any loads we emit are generated in the preceding edge, a
1122 // PHI may reference the global on more than one edge, so every matching
1123 // slot must be handled.
1124 if (auto *PHI = dyn_cast<PHINode>(Insn)) {
1125 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I < E; ++I) {
1126 if (PHI->getIncomingValue(I) != OldGV)
1127 continue;
1128
1129 BasicBlock *IncomingBB = PHI->getIncomingBlock(I);
1130 Builder.SetInsertPoint(IncomingBB->getTerminator());
1131 Builder.SetCurrentDebugLocation(PHI->getDebugLoc());
1132 LoadInst *EdgeLoad = Builder.CreateLoad(NewGV->getType(), NewGV);
1133 PHI->setIncomingValue(I, EdgeLoad);
1134 }
1135 continue;
1136 }
1137
1138 Builder.SetInsertPoint(Insn);
1139 Builder.SetCurrentDebugLocation(Insn->getDebugLoc());
1140 LoadInst *Load = Builder.CreateLoad(NewGV->getType(), NewGV);
1141
1142 // The replacement declare target global lives in the default address
1143 // space, whereas the original global may reside in a non-default
1144 // address space. In that case the initial lowering may have
1145 // emitted an addrspacecast that is no longer valid. Replace the
1146 // whole addrspacecast with the load and erase it rather than
1147 // feeding the load back into the (now pointless) cast.
1148 // NOTE: If we end up with replacement declare target globals in
1149 // non-zero AS's the below will need some minor extensions to have the
1150 // option to alter the address space cast to the new address space where
1151 // required rather than just replacing it.
1152 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Insn)) {
1153 unsigned NewGVAS = NewGV->getType()->getPointerAddressSpace();
1154 assert(NewGVAS == 0 &&
1155 "Non-default address space declare target global");
1156 unsigned OldGVAS = OldGV->getType()->getPointerAddressSpace();
1157 unsigned DestAS = ASC->getType()->getPointerAddressSpace();
1158 if (DestAS == 0 && NewGVAS != OldGVAS) {
1159 ASC->replaceAllUsesWith(Load);
1160 ASC->eraseFromParent();
1161 continue;
1162 }
1163 }
1164
1165 Insn->replaceUsesOfWith(OldGV, Load);
1166 }
1167 }
1168
1170}
1171
1173 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1174}
1175
1177 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1178 auto *GV =
1179 new GlobalVariable(M, I32Ty,
1180 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1181 ConstantInt::get(I32Ty, Value), Name);
1182 GV->setVisibility(GlobalValue::HiddenVisibility);
1183
1184 return GV;
1185}
1186
1188 if (List.empty())
1189 return;
1190
1191 // Convert List to what ConstantArray needs.
1193 UsedArray.resize(List.size());
1194 for (unsigned I = 0, E = List.size(); I != E; ++I)
1196 cast<Constant>(&*List[I]), Builder.getPtrTy());
1197
1198 if (UsedArray.empty())
1199 return;
1200 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1201
1202 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1203 ConstantArray::get(ATy, UsedArray), Name);
1204
1205 GV->setSection("llvm.metadata");
1206}
1207
1210 OMPTgtExecModeFlags Mode) {
1211 auto *Int8Ty = Builder.getInt8Ty();
1212 auto *GVMode = new GlobalVariable(
1213 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1214 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1215 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1216 return GVMode;
1217}
1218
1220 uint32_t SrcLocStrSize,
1221 IdentFlag LocFlags,
1222 unsigned Reserve2Flags) {
1223 // Enable "C-mode".
1224 LocFlags |= OMP_IDENT_FLAG_KMPC;
1225
1226 Constant *&Ident =
1227 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1228 if (!Ident) {
1229 Constant *I32Null = ConstantInt::getNullValue(Int32);
1230 Constant *IdentData[] = {I32Null,
1231 ConstantInt::get(Int32, uint32_t(LocFlags)),
1232 ConstantInt::get(Int32, Reserve2Flags),
1233 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1234
1235 size_t SrcLocStrArgIdx = 4;
1236 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1238 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1239 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1240 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1241 Constant *Initializer =
1242 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1243
1244 // Look for existing encoding of the location + flags, not needed but
1245 // minimizes the difference to the existing solution while we transition.
1246 for (GlobalVariable &GV : M.globals())
1247 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1248 if (GV.getInitializer() == Initializer)
1249 Ident = &GV;
1250
1251 if (!Ident) {
1252 auto *GV = new GlobalVariable(
1253 M, OpenMPIRBuilder::Ident,
1254 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1256 M.getDataLayout().getDefaultGlobalsAddressSpace());
1257 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1258 GV->setAlignment(Align(8));
1259 Ident = GV;
1260 }
1261 }
1262
1263 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1264}
1265
1267 uint32_t &SrcLocStrSize) {
1268 SrcLocStrSize = LocStr.size();
1269 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1270 if (!SrcLocStr) {
1271 Constant *Initializer =
1272 ConstantDataArray::getString(M.getContext(), LocStr);
1273
1274 // Look for existing encoding of the location, not needed but minimizes the
1275 // difference to the existing solution while we transition.
1276 for (GlobalVariable &GV : M.globals())
1277 if (GV.isConstant() && GV.hasInitializer() &&
1278 GV.getInitializer() == Initializer)
1279 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1280
1281 SrcLocStr = Builder.CreateGlobalString(
1282 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1283 &M);
1284 }
1285 return SrcLocStr;
1286}
1287
1289 StringRef FileName,
1290 unsigned Line, unsigned Column,
1291 uint32_t &SrcLocStrSize) {
1292 SmallString<128> Buffer;
1293 Buffer.push_back(';');
1294 Buffer.append(FileName);
1295 Buffer.push_back(';');
1296 Buffer.append(FunctionName);
1297 Buffer.push_back(';');
1298 Buffer.append(std::to_string(Line));
1299 Buffer.push_back(';');
1300 Buffer.append(std::to_string(Column));
1301 Buffer.push_back(';');
1302 Buffer.push_back(';');
1303 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1304}
1305
1306Constant *
1308 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1309 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1310}
1311
1313 uint32_t &SrcLocStrSize,
1314 Function *F) {
1315 DILocation *DIL = DL.get();
1316 if (!DIL)
1317 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1318 StringRef FileName =
1319 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1320 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1321 if (Function.empty() && F)
1322 Function = F->getName();
1323 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1324 DIL->getColumn(), SrcLocStrSize);
1325}
1326
1328 uint32_t &SrcLocStrSize) {
1329 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1330 Loc.IP.getBlock()->getParent());
1331}
1332
1335 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1336 "omp_global_thread_num");
1337}
1338
1339OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1340 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1341 ArrayRef<Type *> ResultPtrTys,
1342 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1343 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1344 "expected one result pointer type per in_reduction item");
1345 if (!updateToLocation(Loc))
1346 return Loc.IP;
1347 if (OrigPtrs.empty())
1348 return Builder.saveIP();
1349
1350 // Compute the executing thread's gtid once for the whole target body and
1351 // reuse it for every in_reduction lookup, so a target with several
1352 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1353 // item.
1354 uint32_t SrcLocStrSize;
1355 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1356 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1357 Value *Gtid = getOrCreateThreadID(Ident);
1358
1359 // The runtime entry point takes (and returns) a generic, default-address-
1360 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1361 // taskgroups to find the matching task_reduction registration for the item.
1362 Type *PtrTy = PointerType::getUnqual(M.getContext());
1363 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1364 FunctionCallee GetThData =
1365 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1366
1367 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1368 // Normalize a non-default-address-space original pointer to the generic
1369 // address space before the call.
1370 Value *OrigPtr = OrigPtrs[Idx];
1371 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1372 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1373 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1374
1375 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1376 "omp.inred.priv");
1377
1378 // Cast the returned private pointer back to the requested address space
1379 // when it differs.
1380 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1381 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1382 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1383
1384 MapPrivateCB(Idx, Priv);
1385 }
1386 return Builder.saveIP();
1387}
1388
1391 bool ForceSimpleCall, bool CheckCancelFlag) {
1392 if (!updateToLocation(Loc))
1393 return Loc.IP;
1394
1395 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1396 // __kmpc_barrier(loc, thread_id);
1397
1398 IdentFlag BarrierLocFlags;
1399 switch (Kind) {
1400 case OMPD_for:
1401 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1402 break;
1403 case OMPD_sections:
1404 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1405 break;
1406 case OMPD_single:
1407 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1408 break;
1409 case OMPD_barrier:
1410 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1411 break;
1412 default:
1413 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1414 break;
1415 }
1416
1417 uint32_t SrcLocStrSize;
1418 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1419 Value *Args[] = {
1420 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1421 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1422
1423 // If we are in a cancellable parallel region, barriers are cancellation
1424 // points.
1425 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1426 bool UseCancelBarrier =
1427 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1428
1430 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1431 ? OMPRTL___kmpc_cancel_barrier
1432 : OMPRTL___kmpc_barrier),
1433 Args);
1434
1435 if (UseCancelBarrier && CheckCancelFlag)
1436 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1437 return Err;
1438
1439 return Builder.saveIP();
1440}
1441
1444 Value *IfCondition,
1445 omp::Directive CanceledDirective) {
1446 if (!updateToLocation(Loc))
1447 return Loc.IP;
1448
1449 // LLVM utilities like blocks with terminators.
1450 auto *UI = Builder.CreateUnreachable();
1451
1452 Instruction *ThenTI = UI, *ElseTI = nullptr;
1453 if (IfCondition) {
1454 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1455
1456 // Even if the if condition evaluates to false, this should count as a
1457 // cancellation point
1458 Builder.SetInsertPoint(ElseTI);
1459 auto ElseIP = Builder.saveIP();
1460
1462 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1463 if (!IPOrErr)
1464 return IPOrErr;
1465 }
1466
1467 Builder.SetInsertPoint(ThenTI);
1468
1469 Value *CancelKind = nullptr;
1470 switch (CanceledDirective) {
1471#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1472 case DirectiveEnum: \
1473 CancelKind = Builder.getInt32(Value); \
1474 break;
1475#include "llvm/Frontend/OpenMP/OMPKinds.def"
1476 default:
1477 llvm_unreachable("Unknown cancel kind!");
1478 }
1479
1480 uint32_t SrcLocStrSize;
1481 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1482 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1483 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1485 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1486
1487 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1488 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1489 return Err;
1490
1491 // Update the insertion point and remove the terminator we introduced.
1492 Builder.SetInsertPoint(UI->getParent());
1493 UI->eraseFromParent();
1494
1495 return Builder.saveIP();
1496}
1497
1500 omp::Directive CanceledDirective) {
1501 if (!updateToLocation(Loc))
1502 return Loc.IP;
1503
1504 // LLVM utilities like blocks with terminators.
1505 auto *UI = Builder.CreateUnreachable();
1506 Builder.SetInsertPoint(UI);
1507
1508 Value *CancelKind = nullptr;
1509 switch (CanceledDirective) {
1510#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1511 case DirectiveEnum: \
1512 CancelKind = Builder.getInt32(Value); \
1513 break;
1514#include "llvm/Frontend/OpenMP/OMPKinds.def"
1515 default:
1516 llvm_unreachable("Unknown cancel kind!");
1517 }
1518
1519 uint32_t SrcLocStrSize;
1520 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1521 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1522 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1524 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1525
1526 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1527 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1528 return Err;
1529
1530 // Update the insertion point and remove the terminator we introduced.
1531 Builder.SetInsertPoint(UI->getParent());
1532 UI->eraseFromParent();
1533
1534 return Builder.saveIP();
1535}
1536
1538 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1539 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1540 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1541 if (!updateToLocation(Loc))
1542 return Loc.IP;
1543
1544 Builder.restoreIP(AllocaIP);
1545 auto *KernelArgsPtr =
1546 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1548
1549 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1550 llvm::Value *Arg =
1551 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1552 Builder.CreateAlignedStore(
1553 KernelArgs[I], Arg,
1554 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1555 }
1556
1557 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1558 NumThreads, HostPtr, KernelArgsPtr};
1559
1561 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1562 OffloadingArgs);
1563
1564 return Builder.saveIP();
1565}
1566
1568 const LocationDescription &Loc, Value *OutlinedFnID,
1569 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1570 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1571
1572 if (!updateToLocation(Loc))
1573 return Loc.IP;
1574
1575 // On top of the arrays that were filled up, the target offloading call
1576 // takes as arguments the device id as well as the host pointer. The host
1577 // pointer is used by the runtime library to identify the current target
1578 // region, so it only has to be unique and not necessarily point to
1579 // anything. It could be the pointer to the outlined function that
1580 // implements the target region, but we aren't using that so that the
1581 // compiler doesn't need to keep that, and could therefore inline the host
1582 // function if proven worthwhile during optimization.
1583
1584 // From this point on, we need to have an ID of the target region defined.
1585 assert(OutlinedFnID && "Invalid outlined function ID!");
1586 (void)OutlinedFnID;
1587
1588 // Return value of the runtime offloading call.
1589 Value *Return = nullptr;
1590
1591 // Arguments for the target kernel.
1592 SmallVector<Value *> ArgsVector;
1593 getKernelArgsVector(Args, Builder, ArgsVector);
1594
1595 // The target region is an outlined function launched by the runtime
1596 // via calls to __tgt_target_kernel().
1597 //
1598 // Note that on the host and CPU targets, the runtime implementation of
1599 // these calls simply call the outlined function without forking threads.
1600 // The outlined functions themselves have runtime calls to
1601 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1602 // the compiler in emitTeamsCall() and emitParallelCall().
1603 //
1604 // In contrast, on the NVPTX target, the implementation of
1605 // __tgt_target_teams() launches a GPU kernel with the requested number
1606 // of teams and threads so no additional calls to the runtime are required.
1607 // Check the error code and execute the host version if required.
1608 Builder.restoreIP(emitTargetKernel(
1609 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1610 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1611
1612 BasicBlock *OffloadFailedBlock =
1613 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1614 BasicBlock *OffloadContBlock =
1615 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1616 Value *Failed = Builder.CreateIsNotNull(Return);
1617 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1618
1619 auto CurFn = Builder.GetInsertBlock()->getParent();
1620 emitBlock(OffloadFailedBlock, CurFn);
1621 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1622 if (!AfterIP)
1623 return AfterIP.takeError();
1624 Builder.restoreIP(*AfterIP);
1625 emitBranch(OffloadContBlock);
1626 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1627 return Builder.saveIP();
1628}
1629
1631 Value *CancelFlag, omp::Directive CanceledDirective) {
1632 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1633 "Unexpected cancellation!");
1634
1635 // For a cancel barrier we create two new blocks.
1636 BasicBlock *BB = Builder.GetInsertBlock();
1637 BasicBlock *NonCancellationBlock;
1638 if (Builder.GetInsertPoint() == BB->end()) {
1639 // TODO: This branch will not be needed once we moved to the
1640 // OpenMPIRBuilder codegen completely.
1641 NonCancellationBlock = BasicBlock::Create(
1642 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1643 } else {
1644 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1646 Builder.SetInsertPoint(BB);
1647 }
1648 BasicBlock *CancellationBlock = BasicBlock::Create(
1649 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1650
1651 // Jump to them based on the return value.
1652 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1653 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1654 /* TODO weight */ nullptr, nullptr);
1655
1656 // From the cancellation block we finalize all variables and go to the
1657 // post finalization block that is known to the FiniCB callback.
1658 auto &FI = FinalizationStack.back();
1659 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1660 if (!FiniBBOrErr)
1661 return FiniBBOrErr.takeError();
1662 Builder.SetInsertPoint(CancellationBlock);
1663 Builder.CreateBr(*FiniBBOrErr);
1664
1665 // The continuation block is where code generation continues.
1666 Builder.SetInsertPoint(NonCancellationBlock, NonCancellationBlock->begin());
1667 return Error::success();
1668}
1669
1670/// Create wrapper function used to gather the outlined function's argument
1671/// structure from a shared buffer and to forward them to it when running in
1672/// Generic mode.
1673///
1674/// The outlined function is expected to receive 2 integer arguments followed by
1675/// an optional pointer argument to an argument structure holding the rest.
1677 Function &OutlinedFn) {
1678 size_t NumArgs = OutlinedFn.arg_size();
1679 assert((NumArgs == 2 || NumArgs == 3) &&
1680 "expected a 2-3 argument parallel outlined function");
1681 bool UseArgStruct = NumArgs == 3;
1682
1683 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1684 IRBuilder<>::InsertPointGuard IPG(Builder);
1685 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1686 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1687 /*isVarArg=*/false);
1688 auto *WrapperFn =
1690 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1691
1692 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1693 WrapperFn->addParamAttr(0, Attribute::ZExt);
1694 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1695
1696 BasicBlock *EntryBB =
1697 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1698 Builder.SetInsertPoint(EntryBB);
1699
1700 // Allocation.
1701 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1702 /*ArraySize=*/nullptr, "addr");
1703 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1704 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1705 AddrAlloca->getName() + ".ascast");
1706
1707 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1708 /*ArraySize=*/nullptr, "zero");
1709 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1710 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1711 ZeroAlloca->getName() + ".ascast");
1712
1713 Value *ArgsAlloca = nullptr;
1714 if (UseArgStruct) {
1715 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1716 /*ArraySize=*/nullptr, "global_args");
1717 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1718 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1719 ArgsAlloca->getName() + ".ascast");
1720 }
1721
1722 // Initialization.
1723 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1724 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1725 if (UseArgStruct) {
1726 Builder.CreateCall(
1727 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1728 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1729 {ArgsAlloca});
1730 }
1731
1732 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1733
1734 // Load structArg from global_args.
1735 if (UseArgStruct) {
1736 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1737 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1738 {Builder.getInt64(0)});
1739 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1740 Args.push_back(StructArg);
1741 }
1742
1743 // Call the outlined function holding the parallel body.
1744 Builder.CreateCall(&OutlinedFn, Args);
1745 Builder.CreateRetVoid();
1746
1747 return WrapperFn;
1748}
1749
1750// Callback used to create OpenMP runtime calls to support
1751// omp parallel clause for the device.
1752// We need to use this callback to replace call to the OutlinedFn in OuterFn
1753// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1755 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1756 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1757 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1758 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1759 assert(OutlinedFn.arg_size() >= 2 &&
1760 "Expected at least tid and bounded tid as arguments");
1761 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1762
1763 // Add some known attributes.
1764 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1765 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1766 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1767 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1768 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1769 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1770
1771 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1772 assert(CI && "Expected call instruction to outlined function");
1773 CI->getParent()->setName("omp_parallel");
1774
1775 Builder.SetInsertPoint(CI);
1776 Type *PtrTy = OMPIRBuilder->VoidPtr;
1777
1778 // Add alloca for kernel args
1779 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1780 Builder.SetInsertPoint(OuterAllocaBB, OuterAllocaBB->getFirstInsertionPt());
1781 AllocaInst *ArgsAlloca =
1782 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1783 Value *Args = ArgsAlloca;
1784 // Add address space cast if array for storing arguments is not allocated
1785 // in address space 0
1786 if (ArgsAlloca->getAddressSpace())
1787 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1788 Builder.restoreIP(CurrentIP);
1789
1790 // Store captured vars which are used by kmpc_parallel_60
1791 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1792 Value *V = *(CI->arg_begin() + 2 + Idx);
1793 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1794 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1795 Builder.CreateStore(V, StoreAddress);
1796 }
1797
1798 Value *Cond =
1799 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1800 : Builder.getInt32(1);
1801 Value *NumThreadsArg =
1802 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1803 : Builder.getInt32(-1);
1804
1805 // If this is not a Generic kernel, we can skip generating the wrapper.
1806 Value *WrapperFn;
1807 if (isGenericKernel(*OuterFn))
1808 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1809 else
1810 WrapperFn = Constant::getNullValue(PtrTy);
1811
1812 // Build kmpc_parallel_60 call
1813 Value *Parallel60CallArgs[] = {
1814 /* identifier*/ Ident,
1815 /* global thread num*/ ThreadID,
1816 /* if expression */ Cond,
1817 /* number of threads */ NumThreadsArg,
1818 /* Proc bind */ Builder.getInt32(-1),
1819 /* outlined function */ &OutlinedFn,
1820 /* wrapper function */ WrapperFn,
1821 /* arguments of the outlined funciton*/ Args,
1822 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1823 /* strict for number of threads */ Builder.getInt32(0)};
1824
1825 FunctionCallee RTLFn =
1826 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1827
1828 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1829
1830 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1831 << *Builder.GetInsertBlock()->getParent() << "\n");
1832
1833 // Initialize the local TID stack location with the argument value.
1834 Builder.SetInsertPoint(PrivTID);
1835 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1836 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1837 PrivTIDAddr);
1838
1839 // Remove redundant call to the outlined function.
1840 CI->eraseFromParent();
1841
1842 for (Instruction *I : ToBeDeleted) {
1843 I->eraseFromParent();
1844 }
1845}
1846
1847// Callback used to create OpenMP runtime calls to support
1848// omp parallel clause for the host.
1849// We need to use this callback to replace call to the OutlinedFn in OuterFn
1850// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1851static void
1853 Function *OuterFn, Value *Ident, Value *IfCondition,
1854 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1855 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1856 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1857 FunctionCallee RTLFn;
1858 if (IfCondition) {
1859 RTLFn =
1860 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1861 } else {
1862 RTLFn =
1863 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1864 }
1865 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1866 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1867 LLVMContext &Ctx = F->getContext();
1868 MDBuilder MDB(Ctx);
1869 // Annotate the callback behavior of the __kmpc_fork_call:
1870 // - The callback callee is argument number 2 (microtask).
1871 // - The first two arguments of the callback callee are unknown (-1).
1872 // - All variadic arguments to the __kmpc_fork_call are passed to the
1873 // callback callee.
1874 F->addMetadata(LLVMContext::MD_callback,
1876 2, {-1, -1},
1877 /* VarArgsArePassed */ true)}));
1878 }
1879 }
1880 // Add some known attributes.
1881 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1882 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1883 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1884
1885 assert(OutlinedFn.arg_size() >= 2 &&
1886 "Expected at least tid and bounded tid as arguments");
1887 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1888
1889 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1890 CI->getParent()->setName("omp_parallel");
1891 Builder.SetInsertPoint(CI);
1892
1893 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1894 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1895 &OutlinedFn};
1896
1897 SmallVector<Value *, 16> RealArgs;
1898 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1899 if (IfCondition) {
1900 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1901 RealArgs.push_back(Cond);
1902 }
1903 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1904
1905 // __kmpc_fork_call_if always expects a void ptr as the last argument
1906 // If there are no arguments, pass a null pointer.
1907 auto PtrTy = OMPIRBuilder->VoidPtr;
1908 if (IfCondition && NumCapturedVars == 0) {
1909 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1910 RealArgs.push_back(NullPtrValue);
1911 }
1912
1913 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1914
1915 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1916 << *Builder.GetInsertBlock()->getParent() << "\n");
1917
1918 // Initialize the local TID stack location with the argument value.
1919 Builder.SetInsertPoint(PrivTID);
1920 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1921 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1922 PrivTIDAddr);
1923
1924 // Remove redundant call to the outlined function.
1925 CI->eraseFromParent();
1926
1927 for (Instruction *I : ToBeDeleted) {
1928 I->eraseFromParent();
1929 }
1930}
1931
1933 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1934 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1935 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1936 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1937 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1938
1939 if (!updateToLocation(Loc))
1940 return Loc.IP;
1941
1942 uint32_t SrcLocStrSize;
1943 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1944 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1945 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1946 (ProcBind != OMP_PROC_BIND_default);
1947 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1948 // If we generate code for the target device, we need to allocate
1949 // struct for aggregate params in the device default alloca address space.
1950 // OpenMP runtime requires that the params of the extracted functions are
1951 // passed as zero address space pointers. This flag ensures that extracted
1952 // function arguments are declared in zero address space
1953 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1954
1955 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1956 // only if we compile for host side.
1957 if (NumThreads && !Config.isTargetDevice()) {
1958 Value *Args[] = {
1959 Ident, ThreadID,
1960 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
1962 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
1963 }
1964
1965 if (ProcBind != OMP_PROC_BIND_default) {
1966 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
1967 Value *Args[] = {
1968 Ident, ThreadID,
1969 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
1971 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
1972 }
1973
1974 BasicBlock *InsertBB = Builder.GetInsertBlock();
1975 Function *OuterFn = InsertBB->getParent();
1976
1977 // Save the outer alloca block because the insertion iterator may get
1978 // invalidated and we still need this later.
1979 BasicBlock *OuterAllocaBlock = OuterAllocIP.getBlock();
1980
1981 // Vector to remember instructions we used only during the modeling but which
1982 // we want to delete at the end.
1984
1985 // Change the location to the outer alloca insertion point to create and
1986 // initialize the allocas we pass into the parallel region.
1987 InsertPointTy NewOuter(OuterAllocaBlock, OuterAllocaBlock->begin());
1988 Builder.restoreIP(NewOuter);
1989 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
1990 AllocaInst *ZeroAddrAlloca =
1991 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
1992 Instruction *TIDAddr = TIDAddrAlloca;
1993 Instruction *ZeroAddr = ZeroAddrAlloca;
1994 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
1995 // Add additional casts to enforce pointers in zero address space
1996 TIDAddr = new AddrSpaceCastInst(
1997 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
1998 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
1999 ToBeDeleted.push_back(TIDAddr);
2000 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2001 PointerType ::get(M.getContext(), 0),
2002 "zero.addr.ascast");
2003 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2004 ToBeDeleted.push_back(ZeroAddr);
2005 }
2006
2007 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2008 // associated arguments in the outlined function, so we delete them later.
2009 ToBeDeleted.push_back(TIDAddrAlloca);
2010 ToBeDeleted.push_back(ZeroAddrAlloca);
2011
2012 // Create an artificial insertion point that will also ensure the blocks we
2013 // are about to split are not degenerated.
2014 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2015
2016 BasicBlock *EntryBB = UI->getParent();
2017 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2018 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2019 BasicBlock *PRegPreFiniBB =
2020 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2021 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2022
2023 auto FiniCBWrapper = [&](InsertPointTy IP) {
2024 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2025 // target to the region exit block.
2026 if (IP.getBlock()->end() == IP.getPoint()) {
2028 Builder.restoreIP(IP);
2029 Instruction *I = Builder.CreateBr(PRegExitBB);
2030 IP = InsertPointTy(I->getParent(), I->getIterator());
2031 }
2032 assert(IP.getBlock()->getTerminator()->getNumSuccessors() == 1 &&
2033 IP.getBlock()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2034 "Unexpected insertion point for finalization call!");
2035 return FiniCB(IP);
2036 };
2037
2038 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2039
2040 // Generate the privatization allocas in the block that will become the entry
2041 // of the outlined function.
2042 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2043 InsertPointTy InnerAllocaIP = Builder.saveIP();
2044
2045 AllocaInst *PrivTIDAddr =
2046 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2047 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2048
2049 // Add some fake uses for OpenMP provided arguments.
2050 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2051 Instruction *ZeroAddrUse =
2052 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2053 ToBeDeleted.push_back(ZeroAddrUse);
2054
2055 // EntryBB
2056 // |
2057 // V
2058 // PRegionEntryBB <- Privatization allocas are placed here.
2059 // |
2060 // V
2061 // PRegionBodyBB <- BodeGen is invoked here.
2062 // |
2063 // V
2064 // PRegPreFiniBB <- The block we will start finalization from.
2065 // |
2066 // V
2067 // PRegionExitBB <- A common exit to simplify block collection.
2068 //
2069
2070 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2071
2072 // Let the caller create the body.
2073 assert(BodyGenCB && "Expected body generation callback!");
2074 InsertPointTy CodeGenIP(PRegBodyBB, PRegBodyBB->begin());
2075 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2076 return Err;
2077
2078 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2079
2080 // If OuterFn is a Generic kernel, we need to use device shared memory to
2081 // allocate argument structures. Otherwise, we use stack allocations as usual.
2082 bool UsesDeviceSharedMemory =
2083 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2084 std::unique_ptr<OutlineInfo> OI =
2085 UsesDeviceSharedMemory
2086 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2087 : std::make_unique<OutlineInfo>();
2088
2089 if (Config.isTargetDevice()) {
2090 // Generate OpenMP target specific runtime call
2091 OI->PostOutlineCB = [=, ToBeDeletedVec =
2092 std::move(ToBeDeleted)](Function &OutlinedFn) {
2093 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2094 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2095 ThreadID, ToBeDeletedVec);
2096 };
2097 } else {
2098 // Generate OpenMP host runtime call
2099 OI->PostOutlineCB = [=, ToBeDeletedVec =
2100 std::move(ToBeDeleted)](Function &OutlinedFn) {
2101 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2102 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2103 };
2104 }
2105
2106 OI->FixUpNonEntryAllocas = true;
2107 OI->OuterAllocBB = OuterAllocaBlock;
2108 OI->EntryBB = PRegEntryBB;
2109 OI->ExitBB = PRegExitBB;
2110 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2111 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2112
2113 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2115 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2116
2117 CodeExtractorAnalysisCache CEAC(*OuterFn);
2118 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2119 /* AggregateArgs */ false,
2120 /* BlockFrequencyInfo */ nullptr,
2121 /* BranchProbabilityInfo */ nullptr,
2122 /* AssumptionCache */ nullptr,
2123 /* AllowVarArgs */ true,
2124 /* AllowAlloca */ true,
2125 /* AllocationBlock */ OuterAllocaBlock,
2126 /* DeallocationBlocks */ {},
2127 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2128
2129 // Find inputs to, outputs from the code region.
2130 BasicBlock *CommonExit = nullptr;
2131 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2132 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2133
2134 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2135 /*CollectGlobalInputs=*/true);
2136
2137 Inputs.remove_if([&](Value *I) {
2139 return GV->getValueType() == OpenMPIRBuilder::Ident;
2140
2141 return false;
2142 });
2143
2144 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2145
2146 FunctionCallee TIDRTLFn =
2147 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2148
2149 auto PrivHelper = [&](Value &V) -> Error {
2150 if (&V == TIDAddr || &V == ZeroAddr) {
2151 OI->ExcludeArgsFromAggregate.push_back(&V);
2152 return Error::success();
2153 }
2154
2156 for (Use &U : V.uses())
2157 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2158 if (ParallelRegionBlockSet.count(UserI->getParent()))
2159 Uses.insert(&U);
2160
2161 // __kmpc_fork_call expects extra arguments as pointers. If the input
2162 // already has a pointer type, everything is fine. Otherwise, store the
2163 // value onto stack and load it back inside the to-be-outlined region. This
2164 // will ensure only the pointer will be passed to the function.
2165 // FIXME: if there are more than 15 trailing arguments, they must be
2166 // additionally packed in a struct.
2167 Value *Inner = &V;
2168 if (!V.getType()->isPointerTy()) {
2170 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2171
2172 Builder.restoreIP(OuterAllocIP);
2173 Value *Ptr;
2174 if (UsesDeviceSharedMemory) {
2175 // Use device shared memory instead, if needed.
2176 Ptr = createOMPAllocShared(OuterAllocIP, V.getType(),
2177 V.getName() + ".reloaded");
2178 for (BasicBlock *DeallocBlock : OuterDeallocBlocks)
2180 InsertPointTy(DeallocBlock, DeallocBlock->getFirstInsertionPt()),
2181 Ptr, V.getType());
2182 } else {
2183 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2184 V.getName() + ".reloaded");
2185 }
2186
2187 // Store to stack at end of the block that currently branches to the entry
2188 // block of the to-be-outlined region.
2189 Builder.SetInsertPoint(InsertBB,
2190 InsertBB->getTerminator()->getIterator());
2191 Builder.CreateStore(&V, Ptr);
2192
2193 // Load back next to allocations in the to-be-outlined region.
2194 Builder.restoreIP(InnerAllocaIP);
2195 Inner = Builder.CreateLoad(V.getType(), Ptr);
2196 }
2197
2198 Value *ReplacementValue = nullptr;
2199 CallInst *CI = dyn_cast<CallInst>(&V);
2200 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2201 ReplacementValue = PrivTID;
2202 } else {
2203 InsertPointOrErrorTy AfterIP =
2204 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2205 if (!AfterIP)
2206 return AfterIP.takeError();
2207 Builder.restoreIP(*AfterIP);
2208 InnerAllocaIP = {
2209 InnerAllocaIP.getBlock(),
2210 InnerAllocaIP.getBlock()->getTerminator()->getIterator()};
2211
2212 assert(ReplacementValue &&
2213 "Expected copy/create callback to set replacement value!");
2214 if (ReplacementValue == &V)
2215 return Error::success();
2216 }
2217
2218 for (Use *UPtr : Uses)
2219 UPtr->set(ReplacementValue);
2220
2221 return Error::success();
2222 };
2223
2224 // Reset the inner alloca insertion as it will be used for loading the values
2225 // wrapped into pointers before passing them into the to-be-outlined region.
2226 // Configure it to insert immediately after the fake use of zero address so
2227 // that they are available in the generated body and so that the
2228 // OpenMP-related values (thread ID and zero address pointers) remain leading
2229 // in the argument list.
2230 InnerAllocaIP = IRBuilder<>::InsertPoint(
2231 ZeroAddrUse->getParent(), ZeroAddrUse->getNextNode()->getIterator());
2232
2233 // Reset the outer alloca insertion point to the entry of the relevant block
2234 // in case it was invalidated.
2235 OuterAllocIP = IRBuilder<>::InsertPoint(
2236 OuterAllocaBlock, OuterAllocaBlock->getFirstInsertionPt());
2237
2238 for (Value *Input : Inputs) {
2239 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2240 if (Error Err = PrivHelper(*Input))
2241 return Err;
2242 }
2243 LLVM_DEBUG({
2244 for (Value *Output : Outputs)
2245 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2246 });
2247 assert(Outputs.empty() &&
2248 "OpenMP outlining should not produce live-out values!");
2249
2250 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2251 LLVM_DEBUG({
2252 for (auto *BB : Blocks)
2253 dbgs() << " PBR: " << BB->getName() << "\n";
2254 });
2255
2256 // Adjust the finalization stack, verify the adjustment, and call the
2257 // finalize function a last time to finalize values between the pre-fini
2258 // block and the exit block if we left the parallel "the normal way".
2259 auto FiniInfo = FinalizationStack.pop_back_val();
2260 (void)FiniInfo;
2261 assert(FiniInfo.DK == OMPD_parallel &&
2262 "Unexpected finalization stack state!");
2263
2264 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2265
2266 InsertPointTy PreFiniIP(PRegPreFiniBB, PRegPreFiniTI->getIterator());
2267 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2268 if (!FiniBBOrErr)
2269 return FiniBBOrErr.takeError();
2270 {
2272 Builder.restoreIP(PreFiniIP);
2273 Builder.CreateBr(*FiniBBOrErr);
2274 // There's currently a branch to omp.par.exit. Delete it. We will get there
2275 // via the fini block
2276 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2277 Term->eraseFromParent();
2278 }
2279
2280 // Register the outlined info.
2281 addOutlineInfo(std::move(OI));
2282
2283 InsertPointTy AfterIP(UI->getParent(), UI->getParent()->end());
2284 UI->eraseFromParent();
2285
2286 return AfterIP;
2287}
2288
2290 // Build call void __kmpc_flush(ident_t *loc)
2291 uint32_t SrcLocStrSize;
2292 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2293 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2294
2296 Args);
2297}
2298
2300 if (!updateToLocation(Loc))
2301 return;
2302 emitFlush(Loc);
2303}
2304
2306 Value *Message) {
2307 if (!updateToLocation(Loc))
2308 return;
2309
2310 // Build call void __kmpc_error(ident_t *loc, int severity,
2311 // const char *message)
2312 uint32_t SrcLocStrSize;
2313 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2314 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2315 // Severity: 1 = warning, 2 = fatal.
2316 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2317 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2318 Value *Args[] = {Ident, Severity, MessageArg};
2319
2321 Args);
2322}
2323
2325 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2326 uint32_t SrcLocStrSize;
2327 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2328 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2329 Constant *I32Null = ConstantInt::getNullValue(Int32);
2330 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2331
2333 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2334}
2335
2341
2343 const DependData &Dep) {
2344 // Store the pointer to the variable
2345 Value *Addr = Builder.CreateStructGEP(
2346 DependInfo, Entry,
2347 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2348 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2349 Builder.CreateStore(DepValPtr, Addr);
2350 // Store the size of the variable
2351 Value *Size = Builder.CreateStructGEP(
2352 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2353 Builder.CreateStore(
2354 ConstantInt::get(SizeTy,
2355 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2356 Size);
2357 // Store the dependency kind
2358 Value *Flags = Builder.CreateStructGEP(
2359 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2360 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2361 static_cast<unsigned int>(Dep.DepKind)),
2362 Flags);
2363}
2364
2365// Processes the dependencies in Dependencies and does the following
2366// - Allocates space on the stack of an array of DependInfo objects
2367// - Populates each DependInfo object with relevant information of
2368// the corresponding dependence.
2369// - All code is inserted in the entry block of the current function.
2371 OpenMPIRBuilder &OMPBuilder,
2373 // Early return if we have no dependencies to process
2374 if (Dependencies.empty())
2375 return nullptr;
2376
2377 // Given a vector of DependData objects, in this function we create an
2378 // array on the stack that holds kmp_depend_info objects corresponding
2379 // to each dependency. This is then passed to the OpenMP runtime.
2380 // For example, if there are 'n' dependencies then the following psedo
2381 // code is generated. Assume the first dependence is on a variable 'a'
2382 //
2383 // \code{c}
2384 // DepArray = alloc(n x sizeof(kmp_depend_info);
2385 // idx = 0;
2386 // DepArray[idx].base_addr = ptrtoint(&a);
2387 // DepArray[idx].len = 8;
2388 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2389 // ++idx;
2390 // DepArray[idx].base_addr = ...;
2391 // \endcode
2392
2393 IRBuilderBase &Builder = OMPBuilder.Builder;
2394 Type *DependInfo = OMPBuilder.DependInfo;
2395
2396 Value *DepArray = nullptr;
2397 OpenMPIRBuilder::InsertPointTy OldIP = Builder.saveIP();
2398 Builder.SetInsertPoint(
2400
2401 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2402 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2403
2404 Builder.restoreIP(OldIP);
2405
2406 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2407 Value *Base =
2408 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2409 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2410 }
2411 return DepArray;
2412}
2413
2415 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2416 // global_tid);
2417 uint32_t SrcLocStrSize;
2418 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2419 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2420 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2421
2422 // Ignore return result until untied tasks are supported.
2424 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2425}
2426
2428 DependenciesInfo Dependencies) {
2429 if (!updateToLocation(Loc))
2430 return;
2431
2432 Value *DepArray = nullptr;
2433 Type *DepArrayTy = nullptr;
2434 Value *NumDeps = nullptr;
2435 if (Dependencies.DepArray) {
2436 DepArray = Dependencies.DepArray;
2437 NumDeps = Dependencies.NumDeps;
2438 } else if (!Dependencies.Deps.empty()) {
2439 InsertPointTy OldIP = Builder.saveIP();
2440 BasicBlock &entryBB =
2441 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2442 Builder.SetInsertPoint(&entryBB, entryBB.getFirstInsertionPt());
2443
2444 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2445 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2446 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2447
2448 Builder.restoreIP(OldIP);
2449 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2450 Value *Base =
2451 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2452 this->emitTaskDependency(Builder, Base, Dep);
2453 }
2454 }
2455
2456 if (DepArray) {
2457 uint32_t SrcLocStrSize;
2458 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2459 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2460 Value *Args[] = {
2461 Ident,
2462 getOrCreateThreadID(Ident),
2463 NumDeps,
2464 DepArray,
2465 ConstantInt::get(Builder.getInt32Ty(), 0),
2467 ConstantInt::get(Builder.getInt32Ty(), false)};
2470 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2471 Args);
2472 } else {
2474 }
2475}
2476
2477/// Create the task duplication function passed to kmpc_taskloop.
2478Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2479 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2480 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2481 if (!DupCB)
2483 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2484
2485 // From OpenMP Runtime p_task_dup_t:
2486 // Routine optionally generated by the compiler for setting the lastprivate
2487 // flag and calling needed constructors for private/firstprivate objects (used
2488 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2489 // lastprivate flag.
2490 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2491
2492 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2493
2494 FunctionType *DupFuncTy = FunctionType::get(
2495 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2496 /*isVarArg=*/false);
2497
2498 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2499 "omp_taskloop_dup", M);
2500 Value *DestTaskArg = DupFunction->getArg(0);
2501 Value *SrcTaskArg = DupFunction->getArg(1);
2502 Value *LastprivateFlagArg = DupFunction->getArg(2);
2503 DestTaskArg->setName("dest_task");
2504 SrcTaskArg->setName("src_task");
2505 LastprivateFlagArg->setName("lastprivate_flag");
2506
2507 IRBuilderBase::InsertPointGuard Guard(Builder);
2508 Builder.SetInsertPoint(
2509 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2510
2511 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2512 Type *TaskWithPrivatesTy =
2513 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2514 Value *TaskPrivates = Builder.CreateGEP(
2515 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2516 Value *ContextPtr = Builder.CreateGEP(
2517 PrivatesTy, TaskPrivates,
2518 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2519 return ContextPtr;
2520 };
2521
2522 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2523 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2524
2525 DestTaskContextPtr->setName("destPtr");
2526 SrcTaskContextPtr->setName("srcPtr");
2527
2528 InsertPointTy AllocaIP(&DupFunction->getEntryBlock(),
2529 DupFunction->getEntryBlock().begin());
2530 InsertPointTy CodeGenIP = Builder.saveIP();
2531 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2532 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2533 if (!AfterIPOrError)
2534 return AfterIPOrError.takeError();
2535 Builder.restoreIP(*AfterIPOrError);
2536
2537 Builder.CreateRetVoid();
2538
2539 return DupFunction;
2540}
2541
2542OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2543 const LocationDescription &Loc, InsertPointTy AllocaIP,
2544 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2545 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2546 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2547 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2548 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2549 Value *TaskContextStructPtrVal) {
2550
2551 if (!updateToLocation(Loc))
2552 return InsertPointTy();
2553
2554 uint32_t SrcLocStrSize;
2555 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2556 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2557
2558 BasicBlock *TaskloopExitBB =
2559 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2560 BasicBlock *TaskloopBodyBB =
2561 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2562 BasicBlock *TaskloopAllocaBB =
2563 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2564
2565 InsertPointTy TaskloopAllocaIP =
2566 InsertPointTy(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2567 InsertPointTy TaskloopBodyIP =
2568 InsertPointTy(TaskloopBodyBB, TaskloopBodyBB->begin());
2569
2570 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2571 return Err;
2572
2573 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2574 if (!result) {
2575 return result.takeError();
2576 }
2577
2578 llvm::CanonicalLoopInfo *CLI = result.get();
2579 auto OI = std::make_unique<OutlineInfo>();
2580 OI->EntryBB = TaskloopAllocaBB;
2581 OI->OuterAllocBB = AllocaIP.getBlock();
2582 OI->ExitBB = TaskloopExitBB;
2583 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2584 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2585
2586 // Add the thread ID argument.
2587 SmallVector<Instruction *> ToBeDeleted;
2588 // dummy instruction to be used as a fake argument
2589 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2590 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2591 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2592 TaskloopAllocaIP, "lb", false, true);
2593 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2594 TaskloopAllocaIP, "ub", false, true);
2595 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2596 TaskloopAllocaIP, "step", false, true);
2597 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2598 // aggregate struct
2599 OI->Inputs.insert(FakeLB);
2600 OI->Inputs.insert(FakeUB);
2601 OI->Inputs.insert(FakeStep);
2602 if (TaskContextStructPtrVal)
2603 OI->Inputs.insert(TaskContextStructPtrVal);
2604 assert(((TaskContextStructPtrVal && DupCB) ||
2605 (!TaskContextStructPtrVal && !DupCB)) &&
2606 "Task context struct ptr and duplication callback must be both set "
2607 "or both null");
2608
2609 // It isn't safe to run the duplication bodygen callback inside the post
2610 // outlining callback so this has to be run now before we know the real task
2611 // shareds structure type.
2612 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2613 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2614 Type *FakeSharedsTy = StructType::get(
2615 Builder.getContext(),
2616 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2617 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2618 FakeSharedsTy,
2619 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2620 if (!TaskDupFnOrErr) {
2621 return TaskDupFnOrErr.takeError();
2622 }
2623 Value *TaskDupFn = *TaskDupFnOrErr;
2624
2625 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2626 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2627 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2628 FakeSharedsTy, Final, Mergeable, Priority,
2629 NumOfCollapseLoops](Function &OutlinedFn) mutable {
2630 // Replace the Stale CI by appropriate RTL function call.
2631 assert(OutlinedFn.hasOneUse() &&
2632 "there must be a single user for the outlined function");
2633 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2634
2635 /* Create the casting for the Bounds Values that can be used when outlining
2636 * to replace the uses of the fakes with real values */
2637 BasicBlock *CodeReplBB = StaleCI->getParent();
2638 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2639 Value *CastedLBVal =
2640 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2641 Value *CastedUBVal =
2642 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2643 Value *CastedStepVal =
2644 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2645
2646 Builder.SetInsertPoint(StaleCI);
2647
2648 // Gather the arguments for emitting the runtime call for
2649 // @__kmpc_omp_task_alloc
2650 Function *TaskAllocFn =
2651 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2652
2653 Value *ThreadID = getOrCreateThreadID(Ident);
2654
2655 if (!NoGroup) {
2656 // Emit runtime call for @__kmpc_taskgroup
2657 Function *TaskgroupFn =
2658 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2659 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2660 }
2661
2662 // `flags` Argument Configuration
2663 // Task is tied if (Flags & 1) == 1.
2664 // Task is untied if (Flags & 1) == 0.
2665 // Task is final if (Flags & 2) == 2.
2666 // Task is not final if (Flags & 2) == 0.
2667 // Task is mergeable if (Flags & 4) == 4.
2668 // Task is not mergeable if (Flags & 4) == 0.
2669 // Task is priority if (Flags & 32) == 32.
2670 // Task is not priority if (Flags & 32) == 0.
2671 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2672 if (Final)
2673 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2674 if (Mergeable)
2675 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2676 if (Priority)
2677 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2678
2679 Value *TaskSize = Builder.getInt64(
2680 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2681
2682 AllocaInst *ArgStructAlloca =
2684 assert(ArgStructAlloca &&
2685 "Unable to find the alloca instruction corresponding to arguments "
2686 "for extracted function");
2687 std::optional<TypeSize> ArgAllocSize =
2688 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2689 assert(ArgAllocSize &&
2690 "Unable to determine size of arguments for extracted function");
2691 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2692
2693 // Emit the @__kmpc_omp_task_alloc runtime call
2694 // The runtime call returns a pointer to an area where the task captured
2695 // variables must be copied before the task is run (TaskData)
2696 CallInst *TaskData = Builder.CreateCall(
2697 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2698 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2699 /*task_func=*/&OutlinedFn});
2700
2701 Value *Shareds = StaleCI->getArgOperand(1);
2702 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2703 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2704 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2705 SharedsSize);
2706 // Get the pointer to loop lb, ub, step from task ptr
2707 // and set up the lowerbound,upperbound and step values
2708 llvm::Value *Lb = Builder.CreateGEP(
2709 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2710
2711 llvm::Value *Ub = Builder.CreateGEP(
2712 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2713
2714 llvm::Value *Step = Builder.CreateGEP(
2715 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2716 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2717
2718 // set up the arguments for emitting kmpc_taskloop runtime call
2719 // setting values for ifval, nogroup, sched, grainsize, task_dup
2720 Value *IfCondVal =
2721 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2722 : Builder.getInt32(1);
2723 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2724 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2725 Value *NoGroupVal = Builder.getInt32(1);
2726 Value *SchedVal = Builder.getInt32(Sched);
2727 Value *GrainSizeVal =
2728 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2729 : Builder.getInt64(0);
2730 Value *TaskDup = TaskDupFn;
2731
2732 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2733 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2734
2735 // taskloop runtime call
2736 Function *TaskloopFn =
2737 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2738 Builder.CreateCall(TaskloopFn, Args);
2739
2740 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2741 // nogroup is not defined
2742 if (!NoGroup) {
2743 Function *EndTaskgroupFn =
2744 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2745 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2746 }
2747
2748 StaleCI->eraseFromParent();
2749
2750 Builder.SetInsertPoint(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2751
2752 LoadInst *SharedsOutlined =
2753 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2754 OutlinedFn.getArg(1)->replaceUsesWithIf(
2755 SharedsOutlined,
2756 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2757
2758 Value *IV = CLI->getIndVar();
2759 Type *IVTy = IV->getType();
2760 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2761
2762 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2763 // UpperBound. These GEP's can be reused for loading the tasks respective
2764 // bounds.
2765 Value *TaskLB = nullptr;
2766 Value *TaskUB = nullptr;
2767 Value *TaskStep = nullptr;
2768 Value *LoadTaskLB = nullptr;
2769 Value *LoadTaskUB = nullptr;
2770 Value *LoadTaskStep = nullptr;
2771 for (Instruction &I : *TaskloopAllocaBB) {
2772 if (I.getOpcode() == Instruction::GetElementPtr) {
2773 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2774 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2775 switch (CI->getZExtValue()) {
2776 case 0:
2777 TaskLB = &I;
2778 break;
2779 case 1:
2780 TaskUB = &I;
2781 break;
2782 case 2:
2783 TaskStep = &I;
2784 break;
2785 }
2786 }
2787 } else if (I.getOpcode() == Instruction::Load) {
2788 LoadInst &Load = cast<LoadInst>(I);
2789 if (Load.getPointerOperand() == TaskLB) {
2790 assert(TaskLB != nullptr && "Expected value for TaskLB");
2791 LoadTaskLB = &I;
2792 } else if (Load.getPointerOperand() == TaskUB) {
2793 assert(TaskUB != nullptr && "Expected value for TaskUB");
2794 LoadTaskUB = &I;
2795 } else if (Load.getPointerOperand() == TaskStep) {
2796 assert(TaskStep != nullptr && "Expected value for TaskStep");
2797 LoadTaskStep = &I;
2798 }
2799 }
2800 }
2801
2802 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2803
2804 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2805 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2806 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2807 Value *TripCountMinusOne = Builder.CreateSDiv(
2808 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2809 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2810 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2811 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2812 // set the trip count in the CLI
2813 CLI->setTripCount(CastedTripCount);
2814
2815 Builder.SetInsertPoint(CLI->getBody(),
2816 CLI->getBody()->getFirstInsertionPt());
2817
2818 if (NumOfCollapseLoops > 1) {
2819 llvm::SmallVector<User *> UsersToReplace;
2820 // When using the collapse clause, the bounds of the loop have to be
2821 // adjusted to properly represent the iterator of the outer loop.
2822 Value *IVPlusTaskLB = Builder.CreateAdd(
2823 CLI->getIndVar(),
2824 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2825 // To ensure every Use is correctly captured, we first want to record
2826 // which users to replace the value in, and then replace the value.
2827 for (auto IVUse = CLI->getIndVar()->uses().begin();
2828 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2829 User *IVUser = IVUse->getUser();
2830 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2831 if (Op->getOpcode() == Instruction::URem ||
2832 Op->getOpcode() == Instruction::UDiv) {
2833 UsersToReplace.push_back(IVUser);
2834 }
2835 }
2836 }
2837 for (User *User : UsersToReplace) {
2838 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2839 }
2840 } else {
2841 // The canonical loop is generated with a fixed lower bound. We need to
2842 // update the index calculation code to use the task's lower bound. The
2843 // generated code looks like this:
2844 // %omp_loop.iv = phi ...
2845 // ...
2846 // %tmp = mul [type] %omp_loop.iv, step
2847 // %user_index = add [type] tmp, lb
2848 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2849 // of the normalised induction variable:
2850 // 1. This one: converting the normalised IV to the user IV
2851 // 2. The increment (add)
2852 // 3. The comparison against the trip count (icmp)
2853 // (1) is the only use that is a mul followed by an add so this cannot
2854 // match other IR.
2855 assert(CLI->getIndVar()->getNumUses() == 3 &&
2856 "Canonical loop should have exactly three uses of the ind var");
2857 for (User *IVUser : CLI->getIndVar()->users()) {
2858 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2859 if (Mul->getOpcode() == Instruction::Mul) {
2860 for (User *MulUser : Mul->users()) {
2861 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2862 if (Add->getOpcode() == Instruction::Add) {
2863 Add->setOperand(1, CastedTaskLB);
2864 }
2865 }
2866 }
2867 }
2868 }
2869 }
2870 }
2871
2872 FakeLB->replaceAllUsesWith(CastedLBVal);
2873 FakeUB->replaceAllUsesWith(CastedUBVal);
2874 FakeStep->replaceAllUsesWith(CastedStepVal);
2875 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2876 I->eraseFromParent();
2877 }
2878 };
2879
2880 addOutlineInfo(std::move(OI));
2881 Builder.SetInsertPoint(TaskloopExitBB, TaskloopExitBB->begin());
2882 return Builder.saveIP();
2883}
2884
2887 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2889 llvm::Type::getInt32Ty(M.getContext()));
2890}
2891
2893 const LocationDescription &Loc, InsertPointTy AllocaIP,
2894 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2895 bool Tied, Value *Final, Value *IfCondition,
2896 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2897 bool Mergeable, Value *EventHandle, Value *Priority) {
2898
2899 if (!updateToLocation(Loc))
2900 return InsertPointTy();
2901
2902 uint32_t SrcLocStrSize;
2903 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2904 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2905 // The current basic block is split into four basic blocks. After outlining,
2906 // they will be mapped as follows:
2907 // ```
2908 // def current_fn() {
2909 // current_basic_block:
2910 // br label %task.exit
2911 // task.exit:
2912 // ; instructions after task
2913 // }
2914 // def outlined_fn() {
2915 // task.alloca:
2916 // br label %task.body
2917 // task.body:
2918 // ret void
2919 // }
2920 // ```
2921 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2922 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2923 BasicBlock *TaskAllocaBB =
2924 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2925
2926 InsertPointTy TaskAllocaIP =
2927 InsertPointTy(TaskAllocaBB, TaskAllocaBB->begin());
2928 InsertPointTy TaskBodyIP = InsertPointTy(TaskBodyBB, TaskBodyBB->begin());
2929 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2930 return Err;
2931
2932 auto OI = std::make_unique<OutlineInfo>();
2933 OI->EntryBB = TaskAllocaBB;
2934 OI->OuterAllocBB = AllocaIP.getBlock();
2935 OI->ExitBB = TaskExitBB;
2936 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2937 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2938
2939 // Add the thread ID argument.
2941 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2942 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2943
2944 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2945 Affinities, Mergeable, Priority, EventHandle,
2946 TaskAllocaBB,
2947 ToBeDeleted](Function &OutlinedFn) mutable {
2948 // Replace the Stale CI by appropriate RTL function call.
2949 assert(OutlinedFn.hasOneUse() &&
2950 "there must be a single user for the outlined function");
2951 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2952
2953 // HasShareds is true if any variables are captured in the outlined region,
2954 // false otherwise.
2955 bool HasShareds = StaleCI->arg_size() > 1;
2956 Builder.SetInsertPoint(StaleCI);
2957
2958 // Gather the arguments for emitting the runtime call for
2959 // @__kmpc_omp_task_alloc
2960 Function *TaskAllocFn =
2961 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2962
2963 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
2964 // call.
2965 Value *ThreadID = getOrCreateThreadID(Ident);
2966
2967 // Argument - `flags`
2968 // Task is tied iff (Flags & 1) == 1.
2969 // Task is untied iff (Flags & 1) == 0.
2970 // Task is final iff (Flags & 2) == 2.
2971 // Task is not final iff (Flags & 2) == 0.
2972 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
2973 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
2974 // Task is detachable iff (Flags & 64) == 64.
2975 // Task is not detachable iff (Flags & 64) == 0.
2976 // Task is priority iff (Flags & 32) == 32.
2977 // Task is not priority iff (Flags & 32) == 0.
2978 // TODO: Handle the other flags.
2979 Value *Flags = Builder.getInt32(Tied);
2980 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
2981 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
2982 if (Final) {
2983 Value *FinalFlag =
2984 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
2985 Flags = Builder.CreateOr(FinalFlag, Flags);
2986 }
2987
2988 if (Mergeable || UseMergedIf0Path)
2989 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2990 if (EventHandle)
2991 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
2992 if (Priority)
2993 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2994
2995 // Argument - `sizeof_kmp_task_t` (TaskSize)
2996 // Tasksize refers to the size in bytes of kmp_task_t data structure
2997 // including private vars accessed in task.
2998 // TODO: add kmp_task_t_with_privates (privates)
2999 Value *TaskSize = Builder.getInt64(
3000 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3001
3002 // Argument - `sizeof_shareds` (SharedsSize)
3003 // SharedsSize refers to the shareds array size in the kmp_task_t data
3004 // structure.
3005 Value *SharedsSize = Builder.getInt64(0);
3006 if (HasShareds) {
3007 AllocaInst *ArgStructAlloca =
3009 assert(ArgStructAlloca &&
3010 "Unable to find the alloca instruction corresponding to arguments "
3011 "for extracted function");
3012 std::optional<TypeSize> ArgAllocSize =
3013 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3014 assert(ArgAllocSize &&
3015 "Unable to determine size of arguments for extracted function");
3016 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3017 }
3018 // Emit the @__kmpc_omp_task_alloc runtime call
3019 // The runtime call returns a pointer to an area where the task captured
3020 // variables must be copied before the task is run (TaskData)
3022 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3023 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3024 /*task_func=*/&OutlinedFn});
3025
3026 if (Affinities.Count && Affinities.Info) {
3028 OMPRTL___kmpc_omp_reg_task_with_affinity);
3029
3030 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3031 Affinities.Count, Affinities.Info});
3032 }
3033
3034 // Emit detach clause initialization.
3035 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3036 // task_descriptor);
3037 if (EventHandle) {
3039 OMPRTL___kmpc_task_allow_completion_event);
3040 llvm::Value *EventVal =
3041 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3042 llvm::Value *EventHandleAddr =
3043 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3044 Builder.getPtrTy(0));
3045 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3046 Builder.CreateStore(EventVal, EventHandleAddr);
3047 }
3048 // Copy the arguments for outlined function
3049 if (HasShareds) {
3050 Value *Shareds = StaleCI->getArgOperand(1);
3051 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3052 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3053 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3054 SharedsSize);
3055 }
3056
3057 if (Priority) {
3058 //
3059 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3060 // we populate the priority information into the "kmp_task_t" here
3061 //
3062 // The struct "kmp_task_t" definition is available in kmp.h
3063 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3064 // data2 is used for priority
3065 //
3066 Type *Int32Ty = Builder.getInt32Ty();
3067 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3068 // kmp_task_t* => { ptr }
3069 Type *TaskPtr = StructType::get(VoidPtr);
3070 Value *TaskGEP =
3071 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3072 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3073 Type *TaskStructType = StructType::get(
3074 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3075 Value *PriorityData = Builder.CreateInBoundsGEP(
3076 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3077 // kmp_cmplrdata_t => { ptr, ptr }
3078 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3079 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3080 PriorityData, {Zero, Zero});
3081 Builder.CreateStore(Priority, CmplrData);
3082 }
3083
3084 Value *DepArray = nullptr;
3085 Value *NumDeps = nullptr;
3086 if (Dependencies.DepArray) {
3087 DepArray = Dependencies.DepArray;
3088 NumDeps = Dependencies.NumDeps;
3089 } else if (!Dependencies.Deps.empty()) {
3090 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3091 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3092 }
3093
3094 // In the presence of the `if` clause, the following IR is generated:
3095 // ...
3096 // %data = call @__kmpc_omp_task_alloc(...)
3097 // br i1 %if_condition, label %then, label %else
3098 // then:
3099 // call @__kmpc_omp_task(...)
3100 // br label %exit
3101 // else:
3102 // ;; Wait for resolution of dependencies, if any, before
3103 // ;; beginning the task
3104 // call @__kmpc_omp_wait_deps(...)
3105 // call @__kmpc_omp_task_begin_if0(...)
3106 // call @outlined_fn(...)
3107 // call @__kmpc_omp_task_complete_if0(...)
3108 // br label %exit
3109 // exit:
3110 // ...
3111 if (IfCondition && !UseMergedIf0Path) {
3112 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3113 // terminator.
3114 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3115 Instruction *IfTerminator =
3116 Builder.GetInsertPoint()->getParent()->getTerminator();
3117 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3118 Builder.SetInsertPoint(IfTerminator);
3119 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3120 &ElseTI);
3121 Builder.SetInsertPoint(ElseTI);
3122
3123 if (DepArray) {
3124 Function *TaskWaitFn =
3125 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3127 TaskWaitFn,
3128 {Ident, ThreadID, NumDeps, DepArray,
3129 ConstantInt::get(Builder.getInt32Ty(), 0),
3131 }
3132 Function *TaskBeginFn =
3133 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3134 Function *TaskCompleteFn =
3135 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3136 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3137 CallInst *CI = nullptr;
3138 if (HasShareds)
3139 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3140 else
3141 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3142 CI->setDebugLoc(StaleCI->getDebugLoc());
3143 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3144 Builder.SetInsertPoint(ThenTI);
3145 }
3146
3147 if (DepArray) {
3148 Function *TaskFn =
3149 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3151 TaskFn,
3152 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3153 ConstantInt::get(Builder.getInt32Ty(), 0),
3155
3156 } else {
3157 // Emit the @__kmpc_omp_task runtime call to spawn the task
3158 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3159 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3160 }
3161
3162 StaleCI->eraseFromParent();
3163
3164 Builder.SetInsertPoint(TaskAllocaBB, TaskAllocaBB->begin());
3165 if (HasShareds) {
3166 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3167 OutlinedFn.getArg(1)->replaceUsesWithIf(
3168 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3169 }
3170
3171 for (Instruction *I : llvm::reverse(ToBeDeleted))
3172 I->eraseFromParent();
3173 };
3174
3175 addOutlineInfo(std::move(OI));
3176 Builder.SetInsertPoint(TaskExitBB, TaskExitBB->begin());
3177
3178 return Builder.saveIP();
3179}
3180
3182 const LocationDescription &Loc, InsertPointTy AllocaIP,
3183 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3184 if (!updateToLocation(Loc))
3185 return InsertPointTy();
3186
3187 uint32_t SrcLocStrSize;
3188 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3189 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3190 Value *ThreadID = getOrCreateThreadID(Ident);
3191
3192 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3193 Function *TaskgroupFn =
3194 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3195 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3196
3197 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3198 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3199 return Err;
3200
3201 Builder.SetInsertPoint(TaskgroupExitBB);
3202 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3203 Function *EndTaskgroupFn =
3204 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3205 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3206
3207 return Builder.saveIP();
3208}
3209
3211 const LocationDescription &Loc, InsertPointTy AllocaIP,
3213 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3214 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3215
3216 if (!updateToLocation(Loc))
3217 return Loc.IP;
3218
3219 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3220
3221 // Each section is emitted as a switch case
3222 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3223 // -> OMP.createSection() which generates the IR for each section
3224 // Iterate through all sections and emit a switch construct:
3225 // switch (IV) {
3226 // case 0:
3227 // <SectionStmt[0]>;
3228 // break;
3229 // ...
3230 // case <NumSection> - 1:
3231 // <SectionStmt[<NumSection> - 1]>;
3232 // break;
3233 // }
3234 // ...
3235 // section_loop.after:
3236 // <FiniCB>;
3237 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3238 Builder.restoreIP(CodeGenIP);
3240 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3241 Function *CurFn = Continue->getParent();
3242 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3243
3244 unsigned CaseNumber = 0;
3245 for (auto SectionCB : SectionCBs) {
3247 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3248 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3249 Builder.SetInsertPoint(CaseBB);
3250 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3251 if (Error Err =
3252 SectionCB(InsertPointTy(),
3253 {CaseEndBr->getParent(), CaseEndBr->getIterator()}, {}))
3254 return Err;
3255 CaseNumber++;
3256 }
3257 // remove the existing terminator from body BB since there can be no
3258 // terminators after switch/case
3259 return Error::success();
3260 };
3261 // Loop body ends here
3262 // LowerBound, UpperBound, and STride for createCanonicalLoop
3263 Type *I32Ty = Type::getInt32Ty(M.getContext());
3264 Value *LB = ConstantInt::get(I32Ty, 0);
3265 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3266 Value *ST = ConstantInt::get(I32Ty, 1);
3268 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3269 if (!LoopInfo)
3270 return LoopInfo.takeError();
3271
3272 InsertPointOrErrorTy WsloopIP =
3273 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3274 WorksharingLoopType::ForStaticLoop, !IsNowait);
3275 if (!WsloopIP)
3276 return WsloopIP.takeError();
3277 InsertPointTy AfterIP = *WsloopIP;
3278
3279 BasicBlock *LoopFini = AfterIP.getBlock()->getSinglePredecessor();
3280 assert(LoopFini && "Bad structure of static workshare loop finalization");
3281
3282 // Apply the finalization callback in LoopAfterBB
3283 auto FiniInfo = FinalizationStack.pop_back_val();
3284 assert(FiniInfo.DK == OMPD_sections &&
3285 "Unexpected finalization stack state!");
3286 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3287 return Err;
3288
3289 return AfterIP;
3290}
3291
3294 BodyGenCallbackTy BodyGenCB,
3295 FinalizeCallbackTy FiniCB) {
3296 if (!updateToLocation(Loc))
3297 return Loc.IP;
3298
3299 auto FiniCBWrapper = [&](InsertPointTy IP) {
3300 if (IP.getBlock()->end() != IP.getPoint())
3301 return FiniCB(IP);
3302 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3303 // will fail because that function requires the Finalization Basic Block to
3304 // have a terminator, which is already removed by EmitOMPRegionBody.
3305 // IP is currently at cancelation block.
3306 // We need to backtrack to the condition block to fetch
3307 // the exit block and create a branch from cancelation
3308 // to exit block.
3310 Builder.restoreIP(IP);
3311 auto *CaseBB = Loc.IP.getBlock();
3312 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3313 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3314 Instruction *I = Builder.CreateBr(ExitBB);
3315 IP = InsertPointTy(I->getParent(), I->getIterator());
3316 return FiniCB(IP);
3317 };
3318
3319 Directive OMPD = Directive::OMPD_sections;
3320 // Since we are using Finalization Callback here, HasFinalize
3321 // and IsCancellable have to be true
3322 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3323 /*Conditional*/ false, /*hasFinalize*/ true,
3324 /*IsCancellable*/ true);
3325}
3326
3332
3333Value *OpenMPIRBuilder::getGPUThreadID() {
3336 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3337 {});
3338}
3339
3340Value *OpenMPIRBuilder::getGPUWarpSize() {
3342 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3343}
3344
3345Value *OpenMPIRBuilder::getNVPTXWarpID() {
3346 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3347 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3348}
3349
3350Value *OpenMPIRBuilder::getNVPTXLaneID() {
3351 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3352 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3353 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3354 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3355 "nvptx_lane_id");
3356}
3357
3358Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3359 Type *ToType) {
3360 Type *FromType = From->getType();
3361 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3362 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3363 assert(FromSize > 0 && "From size must be greater than zero");
3364 assert(ToSize > 0 && "To size must be greater than zero");
3365 if (FromType == ToType)
3366 return From;
3367 if (FromSize == ToSize)
3368 return Builder.CreateBitCast(From, ToType);
3369 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3370 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3371 InsertPointTy SaveIP = Builder.saveIP();
3372 Builder.restoreIP(AllocaIP);
3373 Value *CastItem = Builder.CreateAlloca(ToType);
3374 Builder.restoreIP(SaveIP);
3375
3376 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3377 CastItem, Builder.getPtrTy(0));
3378 Builder.CreateStore(From, ValCastItem);
3379 return Builder.CreateLoad(ToType, CastItem);
3380}
3381
3382Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3383 Value *Element,
3384 Type *ElementType,
3385 Value *Offset) {
3386 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3387 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3388
3389 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3390 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3391 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3392 Value *WarpSize =
3393 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3395 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3396 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3397 Value *WarpSizeCast =
3398 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3399 Value *ShuffleCall =
3400 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3401 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3402 // down to the requested element type, otherwise storing the result would
3403 // write past the end of an element narrower than the shuffle width.
3404 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3405}
3406
3407void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3408 Value *DstAddr, Type *ElemType,
3409 Value *Offset, Type *ReductionArrayTy,
3410 bool IsByRefElem) {
3411 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3412 // Create the loop over the big sized data.
3413 // ptr = (void*)Elem;
3414 // ptrEnd = (void*) Elem + 1;
3415 // Step = 8;
3416 // while (ptr + Step < ptrEnd)
3417 // shuffle((int64_t)*ptr);
3418 // Step = 4;
3419 // while (ptr + Step < ptrEnd)
3420 // shuffle((int32_t)*ptr);
3421 // ...
3422 Type *IndexTy = Builder.getIndexTy(
3423 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3424 Value *ElemPtr = DstAddr;
3425 Value *Ptr = SrcAddr;
3426 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3427 if (Size < IntSize)
3428 continue;
3429 Type *IntType = Builder.getIntNTy(IntSize * 8);
3430 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3431 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3432 Value *SrcAddrGEP =
3433 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3434 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3435 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3436
3437 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3438 if ((Size / IntSize) > 1) {
3439 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3440 SrcAddrGEP, Builder.getPtrTy());
3441 BasicBlock *PreCondBB =
3442 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3443 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3444 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3445 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3446 emitBlock(PreCondBB, CurFunc);
3447 PHINode *PhiSrc =
3448 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3449 PhiSrc->addIncoming(Ptr, CurrentBB);
3450 PHINode *PhiDest =
3451 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3452 PhiDest->addIncoming(ElemPtr, CurrentBB);
3453 Ptr = PhiSrc;
3454 ElemPtr = PhiDest;
3455 Value *PtrDiff = Builder.CreatePtrDiff(
3456 Builder.getInt8Ty(), PtrEnd,
3457 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3458 Builder.CreateCondBr(
3459 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3460 ExitBB);
3461 emitBlock(ThenBB, CurFunc);
3462 Value *Res = createRuntimeShuffleFunction(
3463 AllocaIP,
3464 Builder.CreateAlignedLoad(
3465 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3466 IntType, Offset);
3467 Builder.CreateAlignedStore(Res, ElemPtr,
3468 M.getDataLayout().getPrefTypeAlign(ElemType));
3469 Value *LocalPtr =
3470 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3471 Value *LocalElemPtr =
3472 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3473 PhiSrc->addIncoming(LocalPtr, ThenBB);
3474 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3475 emitBranch(PreCondBB);
3476 emitBlock(ExitBB, CurFunc);
3477 } else {
3478 // The shuffled value comes back as the chunk's integer type, so the
3479 // store covers exactly this chunk regardless of what ElemType is.
3480 Value *Res = createRuntimeShuffleFunction(
3481 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3482 Builder.CreateStore(Res, ElemPtr);
3483 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3484 ElemPtr =
3485 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3486 }
3487 Size = Size % IntSize;
3488 }
3489}
3490
3491Error OpenMPIRBuilder::emitReductionListCopy(
3492 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3493 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3494 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3495 Type *IndexTy = Builder.getIndexTy(
3496 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3497 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3498
3499 // Iterates, element-by-element, through the source Reduce list and
3500 // make a copy.
3501 for (auto En : enumerate(ReductionInfos)) {
3502 const ReductionInfo &RI = En.value();
3503 Value *SrcElementAddr = nullptr;
3504 AllocaInst *DestAlloca = nullptr;
3505 Value *DestElementAddr = nullptr;
3506 Value *DestElementPtrAddr = nullptr;
3507 // Should we shuffle in an element from a remote lane?
3508 bool ShuffleInElement = false;
3509 // Set to true to update the pointer in the dest Reduce list to a
3510 // newly created element.
3511 bool UpdateDestListPtr = false;
3512
3513 // Step 1.1: Get the address for the src element in the Reduce list.
3514 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3515 ReductionArrayTy, SrcBase,
3516 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3517 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3518
3519 // Step 1.2: Create a temporary to store the element in the destination
3520 // Reduce list.
3521 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3522 ReductionArrayTy, DestBase,
3523 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3524 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3525 switch (Action) {
3527 InsertPointTy CurIP = Builder.saveIP();
3528 Builder.restoreIP(AllocaIP);
3529
3530 Type *DestAllocaType =
3531 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3532 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3533 ".omp.reduction.element");
3534 DestAlloca->setAlignment(
3535 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3536 DestElementAddr = DestAlloca;
3537 DestElementAddr =
3538 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3539 DestElementAddr->getName() + ".ascast");
3540 Builder.restoreIP(CurIP);
3541 ShuffleInElement = true;
3542 UpdateDestListPtr = true;
3543 break;
3544 }
3546 DestElementAddr =
3547 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3548 break;
3549 }
3550 }
3551
3552 // Now that all active lanes have read the element in the
3553 // Reduce list, shuffle over the value from the remote lane.
3554 if (ShuffleInElement) {
3555 Type *ShuffleType = RI.ElementType;
3556 Value *ShuffleSrcAddr = SrcElementAddr;
3557 Value *ShuffleDestAddr = DestElementAddr;
3558 AllocaInst *LocalStorage = nullptr;
3559
3560 if (IsByRefElem) {
3561 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3562 assert(RI.ByRefAllocatedType &&
3563 "Expected by-ref allocated type to be set");
3564 // For by-ref reductions, we need to copy from the remote lane the
3565 // actual value of the partial reduction computed by that remote lane;
3566 // rather than, for example, a pointer to that data or, even worse, a
3567 // pointer to the descriptor of the by-ref reduction element.
3568 ShuffleType = RI.ByRefElementType;
3569
3570 if (RI.DataPtrPtrGen) {
3571 // Descriptor-based by-ref: extract data pointer from descriptor.
3572 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3573 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3574
3575 if (!GenResult)
3576 return GenResult.takeError();
3577
3578 ShuffleSrcAddr =
3579 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3580
3581 {
3582 InsertPointTy OldIP = Builder.saveIP();
3583 Builder.restoreIP(AllocaIP);
3584
3585 LocalStorage = Builder.CreateAlloca(ShuffleType);
3586 Builder.restoreIP(OldIP);
3587 ShuffleDestAddr = LocalStorage;
3588 }
3589 } else {
3590 // Non-descriptor by-ref: the pointer already references data
3591 // directly. Shuffle into the destination alloca.
3592 ShuffleDestAddr = DestElementAddr;
3593 }
3594 }
3595
3596 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3597 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3598
3599 if (IsByRefElem && RI.DataPtrPtrGen) {
3600 // Copy descriptor from source and update base_ptr to shuffled data
3601 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3602 DestAlloca, Builder.getPtrTy(), ".ascast");
3603
3604 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3605 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3606 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3607
3608 if (!GenResult)
3609 return GenResult.takeError();
3610 }
3611 } else {
3612 switch (RI.EvaluationKind) {
3613 case EvalKind::Scalar: {
3614 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3615 // Store the source element value to the dest element address.
3616 Builder.CreateStore(Elem, DestElementAddr);
3617 break;
3618 }
3619 case EvalKind::Complex: {
3620 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3621 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3622 Value *SrcReal = Builder.CreateLoad(
3623 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3624 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3625 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3626 Value *SrcImg = Builder.CreateLoad(
3627 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3628
3629 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3630 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3631 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3632 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3633 Builder.CreateStore(SrcReal, DestRealPtr);
3634 Builder.CreateStore(SrcImg, DestImgPtr);
3635 break;
3636 }
3637 case EvalKind::Aggregate: {
3638 Value *SizeVal = Builder.getInt64(
3639 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3640 Builder.CreateMemCpy(
3641 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3642 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3643 SizeVal, false);
3644 break;
3645 }
3646 };
3647 }
3648
3649 // Step 3.1: Modify reference in dest Reduce list as needed.
3650 // Modifying the reference in Reduce list to point to the newly
3651 // created element. The element is live in the current function
3652 // scope and that of functions it invokes (i.e., reduce_function).
3653 // RemoteReduceData[i] = (void*)&RemoteElem
3654 if (UpdateDestListPtr) {
3655 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3656 DestElementAddr, Builder.getPtrTy(),
3657 DestElementAddr->getName() + ".ascast");
3658 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3659 }
3660 }
3661
3662 return Error::success();
3663}
3664
3665Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3666 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3667 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3668 IRBuilder<>::InsertPointGuard IPG(Builder);
3669 LLVMContext &Ctx = M.getContext();
3670 FunctionType *FuncTy = FunctionType::get(
3671 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3672 /* IsVarArg */ false);
3673 Function *WcFunc =
3675 "_omp_reduction_inter_warp_copy_func", &M);
3676 WcFunc->setCallingConv(Config.getRuntimeCC());
3677 WcFunc->setAttributes(FuncAttrs);
3678 WcFunc->addParamAttr(0, Attribute::NoUndef);
3679 WcFunc->addParamAttr(1, Attribute::NoUndef);
3680 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3681 Builder.SetInsertPoint(EntryBB);
3682 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3683
3684 // ReduceList: thread local Reduce list.
3685 // At the stage of the computation when this function is called, partially
3686 // aggregated values reside in the first lane of every active warp.
3687 Argument *ReduceListArg = WcFunc->getArg(0);
3688 // NumWarps: number of warps active in the parallel region. This could
3689 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3690 Argument *NumWarpsArg = WcFunc->getArg(1);
3691
3692 // This array is used as a medium to transfer, one reduce element at a time,
3693 // the data from the first lane of every warp to lanes in the first warp
3694 // in order to perform the final step of a reduction in a parallel region
3695 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3696 // for reduced latency, as well as to have a distinct copy for concurrently
3697 // executing target regions. The array is declared with common linkage so
3698 // as to be shared across compilation units.
3699 StringRef TransferMediumName =
3700 "__openmp_nvptx_data_transfer_temporary_storage";
3701 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3702 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3703 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3704 if (!TransferMedium) {
3705 TransferMedium = new GlobalVariable(
3706 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3707 UndefValue::get(ArrayTy), TransferMediumName,
3708 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3709 /*AddressSpace=*/3);
3710 }
3711
3712 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3713 Value *GPUThreadID = getGPUThreadID();
3714 // nvptx_lane_id = nvptx_id % warpsize
3715 Value *LaneID = getNVPTXLaneID();
3716 // nvptx_warp_id = nvptx_id / warpsize
3717 Value *WarpID = getNVPTXWarpID();
3718
3719 InsertPointTy AllocaIP =
3720 InsertPointTy(Builder.GetInsertBlock(),
3721 Builder.GetInsertBlock()->getFirstInsertionPt());
3722 Type *Arg0Type = ReduceListArg->getType();
3723 Type *Arg1Type = NumWarpsArg->getType();
3724 Builder.restoreIP(AllocaIP);
3725 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3726 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3727 AllocaInst *NumWarpsAlloca =
3728 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3729 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3730 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3731 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3732 NumWarpsAlloca, Builder.getPtrTy(0),
3733 NumWarpsAlloca->getName() + ".ascast");
3734 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3735 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3736 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3737 InsertPointTy CodeGenIP =
3738 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3739 Builder.restoreIP(CodeGenIP);
3740
3741 Value *ReduceList =
3742 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3743
3744 for (auto En : enumerate(ReductionInfos)) {
3745 //
3746 // Warp master copies reduce element to transfer medium in __shared__
3747 // memory.
3748 //
3749 const ReductionInfo &RI = En.value();
3750 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3751 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3752 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3753 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3754 Type *CType = Builder.getIntNTy(TySize * 8);
3755
3756 unsigned NumIters = RealTySize / TySize;
3757 if (NumIters == 0)
3758 continue;
3759 Value *Cnt = nullptr;
3760 Value *CntAddr = nullptr;
3761 BasicBlock *PrecondBB = nullptr;
3762 BasicBlock *ExitBB = nullptr;
3763 if (NumIters > 1) {
3764 CodeGenIP = Builder.saveIP();
3765 Builder.restoreIP(AllocaIP);
3766 CntAddr =
3767 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3768
3769 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3770 CntAddr->getName() + ".ascast");
3771 Builder.restoreIP(CodeGenIP);
3772 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3773 CntAddr,
3774 /*Volatile=*/false);
3775 PrecondBB = BasicBlock::Create(Ctx, "precond");
3776 ExitBB = BasicBlock::Create(Ctx, "exit");
3777 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3778 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3779 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3780 /*Volatile=*/false);
3781 Value *Cmp = Builder.CreateICmpULT(
3782 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3783 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3784 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3785 }
3786
3787 // kmpc_barrier.
3788 InsertPointOrErrorTy BarrierIP1 =
3790 omp::Directive::OMPD_unknown,
3791 /* ForceSimpleCall */ false,
3792 /* CheckCancelFlag */ true);
3793 if (!BarrierIP1)
3794 return BarrierIP1.takeError();
3795 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3796 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3797 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3798
3799 // if (lane_id == 0)
3800 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3801 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3802 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3803
3804 // Reduce element = LocalReduceList[i]
3805 auto *RedListArrayTy =
3806 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3807 Type *IndexTy = Builder.getIndexTy(
3808 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3809 Value *ElemPtrPtr =
3810 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3811 {ConstantInt::get(IndexTy, 0),
3812 ConstantInt::get(IndexTy, En.index())});
3813 // elemptr = ((CopyType*)(elemptrptr)) + I
3814 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3815
3816 if (IsByRefElem && RI.DataPtrPtrGen) {
3817 InsertPointOrErrorTy GenRes =
3818 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3819
3820 if (!GenRes)
3821 return GenRes.takeError();
3822
3823 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3824 }
3825
3826 if (NumIters > 1)
3827 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3828
3829 // Get pointer to location in transfer medium.
3830 // MediumPtr = &medium[warp_id]
3831 Value *MediumPtr = Builder.CreateInBoundsGEP(
3832 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3833 // elem = *elemptr
3834 //*MediumPtr = elem
3835 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3836 // Store the source element value to the dest element address.
3837 Builder.CreateStore(Elem, MediumPtr,
3838 /*IsVolatile*/ true);
3839 Builder.CreateBr(MergeBB);
3840
3841 // else
3842 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3843 Builder.CreateBr(MergeBB);
3844
3845 // endif
3846 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3847 InsertPointOrErrorTy BarrierIP2 =
3849 omp::Directive::OMPD_unknown,
3850 /* ForceSimpleCall */ false,
3851 /* CheckCancelFlag */ true);
3852 if (!BarrierIP2)
3853 return BarrierIP2.takeError();
3854
3855 // Warp 0 copies reduce element from transfer medium
3856 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3857 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3858 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3859
3860 Value *NumWarpsVal =
3861 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3862 // Up to 32 threads in warp 0 are active.
3863 Value *IsActiveThread =
3864 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3865 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3866
3867 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3868
3869 // SecMediumPtr = &medium[tid]
3870 // SrcMediumVal = *SrcMediumPtr
3871 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3872 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3873 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3874 Value *TargetElemPtrPtr =
3875 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3876 {ConstantInt::get(IndexTy, 0),
3877 ConstantInt::get(IndexTy, En.index())});
3878 Value *TargetElemPtrVal =
3879 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3880 Value *TargetElemPtr = TargetElemPtrVal;
3881
3882 if (IsByRefElem && RI.DataPtrPtrGen) {
3883 InsertPointOrErrorTy GenRes =
3884 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3885
3886 if (!GenRes)
3887 return GenRes.takeError();
3888
3889 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3890 }
3891
3892 if (NumIters > 1)
3893 TargetElemPtr =
3894 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3895
3896 // *TargetElemPtr = SrcMediumVal;
3897 Value *SrcMediumValue =
3898 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3899 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3900 Builder.CreateBr(W0MergeBB);
3901
3902 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3903 Builder.CreateBr(W0MergeBB);
3904
3905 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3906
3907 if (NumIters > 1) {
3908 Cnt = Builder.CreateNSWAdd(
3909 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3910 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3911
3912 auto *CurFn = Builder.GetInsertBlock()->getParent();
3913 emitBranch(PrecondBB);
3914 emitBlock(ExitBB, CurFn);
3915 }
3916 RealTySize %= TySize;
3917 }
3918 }
3919
3920 Builder.CreateRetVoid();
3921
3922 return WcFunc;
3923}
3924
3925Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3926 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3927 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3928 LLVMContext &Ctx = M.getContext();
3929 IRBuilder<>::InsertPointGuard IPG(Builder);
3930 FunctionType *FuncTy =
3931 FunctionType::get(Builder.getVoidTy(),
3932 {Builder.getPtrTy(), Builder.getInt16Ty(),
3933 Builder.getInt16Ty(), Builder.getInt16Ty()},
3934 /* IsVarArg */ false);
3935 Function *SarFunc =
3937 "_omp_reduction_shuffle_and_reduce_func", &M);
3938 SarFunc->setCallingConv(Config.getRuntimeCC());
3939 SarFunc->setAttributes(FuncAttrs);
3940 SarFunc->addParamAttr(0, Attribute::NoUndef);
3941 SarFunc->addParamAttr(1, Attribute::NoUndef);
3942 SarFunc->addParamAttr(2, Attribute::NoUndef);
3943 SarFunc->addParamAttr(3, Attribute::NoUndef);
3944 SarFunc->addParamAttr(1, Attribute::SExt);
3945 SarFunc->addParamAttr(2, Attribute::SExt);
3946 SarFunc->addParamAttr(3, Attribute::SExt);
3947 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3948 Builder.SetInsertPoint(EntryBB);
3949 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3950
3951 // Thread local Reduce list used to host the values of data to be reduced.
3952 Argument *ReduceListArg = SarFunc->getArg(0);
3953 // Current lane id; could be logical.
3954 Argument *LaneIDArg = SarFunc->getArg(1);
3955 // Offset of the remote source lane relative to the current lane.
3956 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
3957 // Algorithm version. This is expected to be known at compile time.
3958 Argument *AlgoVerArg = SarFunc->getArg(3);
3959
3960 Type *ReduceListArgType = ReduceListArg->getType();
3961 Type *LaneIDArgType = LaneIDArg->getType();
3962 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
3963 Value *ReduceListAlloca = Builder.CreateAlloca(
3964 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
3965 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3966 LaneIDArg->getName() + ".addr");
3967 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
3968 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
3969 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3970 AlgoVerArg->getName() + ".addr");
3971 ArrayType *RedListArrayTy =
3972 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3973
3974 // Create a local thread-private variable to host the Reduce list
3975 // from a remote lane.
3976 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
3977 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
3978
3979 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3980 ReduceListAlloca, ReduceListArgType,
3981 ReduceListAlloca->getName() + ".ascast");
3982 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3983 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
3984 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3985 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
3986 RemoteLaneOffsetAlloca->getName() + ".ascast");
3987 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3988 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
3989 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3990 RemoteReductionListAlloca, Builder.getPtrTy(),
3991 RemoteReductionListAlloca->getName() + ".ascast");
3992
3993 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3994 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
3995 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
3996 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
3997
3998 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
3999 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4000 Value *RemoteLaneOffset =
4001 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4002 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4003
4004 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4005
4006 // This loop iterates through the list of reduce elements and copies,
4007 // element by element, from a remote lane in the warp to RemoteReduceList,
4008 // hosted on the thread's stack.
4009 Error EmitRedLsCpRes = emitReductionListCopy(
4010 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4011 ReduceList, RemoteListAddrCast, IsByRef,
4012 {RemoteLaneOffset, nullptr, nullptr});
4013
4014 if (EmitRedLsCpRes)
4015 return EmitRedLsCpRes;
4016
4017 // The actions to be performed on the Remote Reduce list is dependent
4018 // on the algorithm version.
4019 //
4020 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4021 // LaneId % 2 == 0 && Offset > 0):
4022 // do the reduction value aggregation
4023 //
4024 // The thread local variable Reduce list is mutated in place to host the
4025 // reduced data, which is the aggregated value produced from local and
4026 // remote lanes.
4027 //
4028 // Note that AlgoVer is expected to be a constant integer known at compile
4029 // time.
4030 // When AlgoVer==0, the first conjunction evaluates to true, making
4031 // the entire predicate true during compile time.
4032 // When AlgoVer==1, the second conjunction has only the second part to be
4033 // evaluated during runtime. Other conjunctions evaluates to false
4034 // during compile time.
4035 // When AlgoVer==2, the third conjunction has only the second part to be
4036 // evaluated during runtime. Other conjunctions evaluates to false
4037 // during compile time.
4038 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4039 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4040 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4041 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4042 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4043 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4044 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4045 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4046 Value *RemoteOffsetComp =
4047 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4048 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4049 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4050 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4051
4052 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4053 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4054 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4055
4056 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4057 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4058 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4059 ReduceList, Builder.getPtrTy());
4060 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4061 RemoteListAddrCast, Builder.getPtrTy());
4062 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4063 ->addFnAttr(Attribute::NoUnwind);
4064 Builder.CreateBr(MergeBB);
4065
4066 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4067 Builder.CreateBr(MergeBB);
4068
4069 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4070
4071 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4072 // Reduce list.
4073 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4074 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4075 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4076
4077 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4078 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4079 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4080 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4081
4082 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4083
4084 EmitRedLsCpRes = emitReductionListCopy(
4085 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4086 RemoteListAddrCast, ReduceList, IsByRef);
4087
4088 if (EmitRedLsCpRes)
4089 return EmitRedLsCpRes;
4090
4091 Builder.CreateBr(CpyMergeBB);
4092
4093 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4094 Builder.CreateBr(CpyMergeBB);
4095
4096 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4097
4098 Builder.CreateRetVoid();
4099
4100 return SarFunc;
4101}
4102
4104OpenMPIRBuilder::generateReductionDescriptor(
4105 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4106 Type *DescriptorType,
4107 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4108 DataPtrPtrGen) {
4109
4110 // Copy the source descriptor to preserve all metadata (rank, extents,
4111 // strides, etc.)
4112 Value *DescriptorSize =
4113 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4114 Builder.CreateMemCpy(
4115 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4116 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4117 DescriptorSize);
4118
4119 // Update the base pointer field to point to the local shuffled data
4120 Value *DataPtrField;
4121 InsertPointOrErrorTy GenResult =
4122 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4123
4124 if (!GenResult)
4125 return GenResult.takeError();
4126
4127 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4128 DataPtr, Builder.getPtrTy(), ".ascast"),
4129 DataPtrField);
4130
4131 return Builder.saveIP();
4132}
4133
4134Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4135 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4136 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4137 InsertPointTy OldIP = Builder.saveIP();
4138 Builder.restoreIP(AllocaIP);
4139
4140 AllocaInst *DescriptorAlloca =
4141 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4142 DescriptorAlloca->setAlignment(
4143 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4144 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4145 DescriptorAlloca, DescriptorPtrTy,
4146 DescriptorAlloca->getName() + ".ascast");
4147
4148 Builder.restoreIP(OldIP);
4149
4150 InsertPointOrErrorTy GenResult =
4151 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4152 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4153 if (!GenResult)
4154 return GenResult.takeError();
4155
4156 return DescriptorAddr;
4157}
4158
4159Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4160 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4161 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4162 IRBuilder<>::InsertPointGuard IPG(Builder);
4163 LLVMContext &Ctx = M.getContext();
4164 FunctionType *FuncTy = FunctionType::get(
4165 Builder.getVoidTy(),
4166 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4167 /* IsVarArg */ false);
4168 Function *LtGCFunc =
4170 "_omp_reduction_list_to_global_copy_func", &M);
4171 LtGCFunc->setAttributes(FuncAttrs);
4172 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4173 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4174 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4175
4176 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4177 Builder.SetInsertPoint(EntryBlock);
4178 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4179
4180 // Buffer: global reduction buffer.
4181 Argument *BufferArg = LtGCFunc->getArg(0);
4182 // Idx: index of the buffer.
4183 Argument *IdxArg = LtGCFunc->getArg(1);
4184 // ReduceList: thread local Reduce list.
4185 Argument *ReduceListArg = LtGCFunc->getArg(2);
4186
4187 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4188 BufferArg->getName() + ".addr");
4189 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4190 IdxArg->getName() + ".addr");
4191 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4192 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4193 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4194 BufferArgAlloca, Builder.getPtrTy(),
4195 BufferArgAlloca->getName() + ".ascast");
4196 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4197 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4198 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4199 ReduceListArgAlloca, Builder.getPtrTy(),
4200 ReduceListArgAlloca->getName() + ".ascast");
4201
4202 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4203 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4204 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4205
4206 Value *LocalReduceList =
4207 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4208 Value *BufferArgVal =
4209 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4210 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4211 Type *IndexTy = Builder.getIndexTy(
4212 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4213 for (auto En : enumerate(ReductionInfos)) {
4214 const ReductionInfo &RI = En.value();
4215 auto *RedListArrayTy =
4216 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4217 // Reduce element = LocalReduceList[i]
4218 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4219 RedListArrayTy, LocalReduceList,
4220 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4221 // elemptr = ((CopyType*)(elemptrptr)) + I
4222 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4223
4224 // Global = Buffer.VD[Idx];
4225 Value *BufferVD =
4226 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4227 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4228 ReductionsBufferTy, BufferVD, 0, En.index());
4229
4230 switch (RI.EvaluationKind) {
4231 case EvalKind::Scalar: {
4232 Value *TargetElement;
4233
4234 if (IsByRef.empty() || !IsByRef[En.index()]) {
4235 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4236 } else {
4237 if (RI.DataPtrPtrGen) {
4238 InsertPointOrErrorTy GenResult =
4239 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4240
4241 if (!GenResult)
4242 return GenResult.takeError();
4243
4244 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4245 }
4246 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4247 }
4248
4249 Builder.CreateStore(TargetElement, GlobVal);
4250 break;
4251 }
4252 case EvalKind::Complex: {
4253 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4254 RI.ElementType, ElemPtr, 0, 0, ".realp");
4255 Value *SrcReal = Builder.CreateLoad(
4256 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4257 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4258 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4259 Value *SrcImg = Builder.CreateLoad(
4260 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4261
4262 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4263 RI.ElementType, GlobVal, 0, 0, ".realp");
4264 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4265 RI.ElementType, GlobVal, 0, 1, ".imagp");
4266 Builder.CreateStore(SrcReal, DestRealPtr);
4267 Builder.CreateStore(SrcImg, DestImgPtr);
4268 break;
4269 }
4270 case EvalKind::Aggregate: {
4271 Value *SizeVal =
4272 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4273 Builder.CreateMemCpy(
4274 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4275 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4276 break;
4277 }
4278 }
4279 }
4280
4281 Builder.CreateRetVoid();
4282 return LtGCFunc;
4283}
4284
4285Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4286 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4287 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4288 IRBuilder<>::InsertPointGuard IPG(Builder);
4289 LLVMContext &Ctx = M.getContext();
4290 FunctionType *FuncTy = FunctionType::get(
4291 Builder.getVoidTy(),
4292 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4293 /* IsVarArg */ false);
4294 Function *LtGRFunc =
4296 "_omp_reduction_list_to_global_reduce_func", &M);
4297 LtGRFunc->setAttributes(FuncAttrs);
4298 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4299 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4300 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4301
4302 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4303 Builder.SetInsertPoint(EntryBlock);
4304 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4305
4306 // Buffer: global reduction buffer.
4307 Argument *BufferArg = LtGRFunc->getArg(0);
4308 // Idx: index of the buffer.
4309 Argument *IdxArg = LtGRFunc->getArg(1);
4310 // ReduceList: thread local Reduce list.
4311 Argument *ReduceListArg = LtGRFunc->getArg(2);
4312
4313 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4314 BufferArg->getName() + ".addr");
4315 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4316 IdxArg->getName() + ".addr");
4317 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4318 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4319 auto *RedListArrayTy =
4320 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4321
4322 // 1. Build a list of reduction variables.
4323 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4324 Value *LocalReduceList =
4325 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4326
4327 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4328
4329 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4330 BufferArgAlloca, Builder.getPtrTy(),
4331 BufferArgAlloca->getName() + ".ascast");
4332 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4333 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4334 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4335 ReduceListArgAlloca, Builder.getPtrTy(),
4336 ReduceListArgAlloca->getName() + ".ascast");
4337 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4338 LocalReduceList, Builder.getPtrTy(),
4339 LocalReduceList->getName() + ".ascast");
4340
4341 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4342 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4343 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4344
4345 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4346 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4347 Type *IndexTy = Builder.getIndexTy(
4348 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4349 for (auto En : enumerate(ReductionInfos)) {
4350 const ReductionInfo &RI = En.value();
4351
4352 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4353 RedListArrayTy, LocalReduceListAddrCast,
4354 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4355 Value *BufferVD =
4356 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4357 // Global = Buffer.VD[Idx];
4358 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4359 ReductionsBufferTy, BufferVD, 0, En.index());
4360
4361 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4362 // Get source descriptor from the reduce list argument
4363 Value *ReduceList =
4364 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4365 Value *SrcElementPtrPtr =
4366 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4367 {ConstantInt::get(IndexTy, 0),
4368 ConstantInt::get(IndexTy, En.index())});
4369 Value *SrcDescriptorAddr =
4370 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4371
4372 // Copy descriptor from source and update base_ptr to global buffer data
4373 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4374 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4375 if (!ByRefAlloc)
4376 return ByRefAlloc.takeError();
4377
4378 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4379 } else {
4380 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4381 }
4382 }
4383
4384 // Call reduce_function(GlobalReduceList, ReduceList)
4385 Value *ReduceList =
4386 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4387 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4388 ->addFnAttr(Attribute::NoUnwind);
4389 Builder.CreateRetVoid();
4390 return LtGRFunc;
4391}
4392
4393Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4394 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4395 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4396 IRBuilder<>::InsertPointGuard IPG(Builder);
4397 LLVMContext &Ctx = M.getContext();
4398 FunctionType *FuncTy = FunctionType::get(
4399 Builder.getVoidTy(),
4400 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4401 /* IsVarArg */ false);
4402 Function *GtLCFunc =
4404 "_omp_reduction_global_to_list_copy_func", &M);
4405 GtLCFunc->setAttributes(FuncAttrs);
4406 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4407 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4408 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4409
4410 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4411 Builder.SetInsertPoint(EntryBlock);
4412 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4413
4414 // Buffer: global reduction buffer.
4415 Argument *BufferArg = GtLCFunc->getArg(0);
4416 // Idx: index of the buffer.
4417 Argument *IdxArg = GtLCFunc->getArg(1);
4418 // ReduceList: thread local Reduce list.
4419 Argument *ReduceListArg = GtLCFunc->getArg(2);
4420
4421 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4422 BufferArg->getName() + ".addr");
4423 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4424 IdxArg->getName() + ".addr");
4425 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4426 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4427 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4428 BufferArgAlloca, Builder.getPtrTy(),
4429 BufferArgAlloca->getName() + ".ascast");
4430 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4431 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4432 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4433 ReduceListArgAlloca, Builder.getPtrTy(),
4434 ReduceListArgAlloca->getName() + ".ascast");
4435 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4436 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4437 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4438
4439 Value *LocalReduceList =
4440 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4441 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4442 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4443 Type *IndexTy = Builder.getIndexTy(
4444 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4445 for (auto En : enumerate(ReductionInfos)) {
4446 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4447 auto *RedListArrayTy =
4448 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4449 // Reduce element = LocalReduceList[i]
4450 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4451 RedListArrayTy, LocalReduceList,
4452 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4453 // elemptr = ((CopyType*)(elemptrptr)) + I
4454 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4455 // Global = Buffer.VD[Idx];
4456 Value *BufferVD =
4457 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4458 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4459 ReductionsBufferTy, BufferVD, 0, En.index());
4460
4461 switch (RI.EvaluationKind) {
4462 case EvalKind::Scalar: {
4463 Type *ElemType = RI.ElementType;
4464
4465 if (!IsByRef.empty() && IsByRef[En.index()]) {
4466 ElemType = RI.ByRefElementType;
4467 if (RI.DataPtrPtrGen) {
4468 InsertPointOrErrorTy GenResult =
4469 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4470
4471 if (!GenResult)
4472 return GenResult.takeError();
4473
4474 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4475 }
4476 }
4477
4478 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4479 Builder.CreateStore(TargetElement, ElemPtr);
4480 break;
4481 }
4482 case EvalKind::Complex: {
4483 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4484 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4485 Value *SrcReal = Builder.CreateLoad(
4486 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4487 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4488 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4489 Value *SrcImg = Builder.CreateLoad(
4490 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4491
4492 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4493 RI.ElementType, ElemPtr, 0, 0, ".realp");
4494 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4495 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4496 Builder.CreateStore(SrcReal, DestRealPtr);
4497 Builder.CreateStore(SrcImg, DestImgPtr);
4498 break;
4499 }
4500 case EvalKind::Aggregate: {
4501 Value *SizeVal =
4502 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4503 Builder.CreateMemCpy(
4504 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4505 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4506 SizeVal, false);
4507 break;
4508 }
4509 }
4510 }
4511
4512 Builder.CreateRetVoid();
4513 return GtLCFunc;
4514}
4515
4516Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4517 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4518 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4519 IRBuilder<>::InsertPointGuard IPG(Builder);
4520 LLVMContext &Ctx = M.getContext();
4521 auto *FuncTy = FunctionType::get(
4522 Builder.getVoidTy(),
4523 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4524 /* IsVarArg */ false);
4525 Function *GtLRFunc =
4527 "_omp_reduction_global_to_list_reduce_func", &M);
4528 GtLRFunc->setAttributes(FuncAttrs);
4529 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4530 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4531 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4532
4533 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4534 Builder.SetInsertPoint(EntryBlock);
4535 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4536
4537 // Buffer: global reduction buffer.
4538 Argument *BufferArg = GtLRFunc->getArg(0);
4539 // Idx: index of the buffer.
4540 Argument *IdxArg = GtLRFunc->getArg(1);
4541 // ReduceList: thread local Reduce list.
4542 Argument *ReduceListArg = GtLRFunc->getArg(2);
4543
4544 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4545 BufferArg->getName() + ".addr");
4546 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4547 IdxArg->getName() + ".addr");
4548 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4549 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4550 ArrayType *RedListArrayTy =
4551 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4552
4553 // 1. Build a list of reduction variables.
4554 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4555 Value *LocalReduceList =
4556 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4557
4558 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4559
4560 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4561 BufferArgAlloca, Builder.getPtrTy(),
4562 BufferArgAlloca->getName() + ".ascast");
4563 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4564 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4565 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4566 ReduceListArgAlloca, Builder.getPtrTy(),
4567 ReduceListArgAlloca->getName() + ".ascast");
4568 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4569 LocalReduceList, Builder.getPtrTy(),
4570 LocalReduceList->getName() + ".ascast");
4571
4572 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4573 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4574 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4575
4576 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4577 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4578 Type *IndexTy = Builder.getIndexTy(
4579 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4580 for (auto En : enumerate(ReductionInfos)) {
4581 const ReductionInfo &RI = En.value();
4582
4583 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4584 RedListArrayTy, ReductionList,
4585 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4586 // Global = Buffer.VD[Idx];
4587 Value *BufferVD =
4588 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4589 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4590 ReductionsBufferTy, BufferVD, 0, En.index());
4591
4592 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4593 // Get source descriptor from the reduce list
4594 Value *ReduceListVal =
4595 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4596 Value *SrcElementPtrPtr =
4597 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4598 {ConstantInt::get(IndexTy, 0),
4599 ConstantInt::get(IndexTy, En.index())});
4600 Value *SrcDescriptorAddr =
4601 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4602
4603 // Copy descriptor from source and update base_ptr to global buffer data
4604 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4605 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4606 if (!ByRefAlloc)
4607 return ByRefAlloc.takeError();
4608
4609 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4610 } else {
4611 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4612 }
4613 }
4614
4615 // Call reduce_function(ReduceList, GlobalReduceList)
4616 Value *ReduceList =
4617 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4618 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4619 ->addFnAttr(Attribute::NoUnwind);
4620 Builder.CreateRetVoid();
4621 return GtLRFunc;
4622}
4623
4624std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4625 std::string Suffix =
4626 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4627 return (Name + Suffix).str();
4628}
4629
4630Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4631 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4633 AttributeList FuncAttrs) {
4634 IRBuilder<>::InsertPointGuard IPG(Builder);
4635 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4636 {Builder.getPtrTy(), Builder.getPtrTy()},
4637 /* IsVarArg */ false);
4638 std::string Name = getReductionFuncName(ReducerName);
4639 Function *ReductionFunc =
4641 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4642 ReductionFunc->setAttributes(FuncAttrs);
4643 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4644 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4645 BasicBlock *EntryBB =
4646 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4647 Builder.SetInsertPoint(EntryBB);
4648 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4649
4650 // Need to alloca memory here and deal with the pointers before getting
4651 // LHS/RHS pointers out
4652 Value *LHSArrayPtr = nullptr;
4653 Value *RHSArrayPtr = nullptr;
4654 Argument *Arg0 = ReductionFunc->getArg(0);
4655 Argument *Arg1 = ReductionFunc->getArg(1);
4656 Type *Arg0Type = Arg0->getType();
4657 Type *Arg1Type = Arg1->getType();
4658
4659 Value *LHSAlloca =
4660 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4661 Value *RHSAlloca =
4662 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4663 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4664 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4665 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4666 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4667 Builder.CreateStore(Arg0, LHSAddrCast);
4668 Builder.CreateStore(Arg1, RHSAddrCast);
4669 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4670 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4671
4672 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4673 Type *IndexTy = Builder.getIndexTy(
4674 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4675 SmallVector<Value *> LHSPtrs, RHSPtrs;
4676 for (auto En : enumerate(ReductionInfos)) {
4677 const ReductionInfo &RI = En.value();
4678 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4679 RedArrayTy, RHSArrayPtr,
4680 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4681 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4682 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4683 RHSI8Ptr, RI.PrivateVariable->getType(),
4684 RHSI8Ptr->getName() + ".ascast");
4685
4686 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4687 RedArrayTy, LHSArrayPtr,
4688 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4689 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4690 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4691 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4692
4694 LHSPtrs.emplace_back(LHSPtr);
4695 RHSPtrs.emplace_back(RHSPtr);
4696 } else {
4697 Value *LHS = LHSPtr;
4698 Value *RHS = RHSPtr;
4699
4700 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4701 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4702 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4703 }
4704
4705 Value *Reduced;
4706 InsertPointOrErrorTy AfterIP =
4707 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4708 if (!AfterIP)
4709 return AfterIP.takeError();
4710 if (!Builder.GetInsertBlock())
4711 return ReductionFunc;
4712
4713 Builder.restoreIP(*AfterIP);
4714
4715 if (!IsByRef.empty() && !IsByRef[En.index()])
4716 Builder.CreateStore(Reduced, LHSPtr);
4717 }
4718 }
4719
4721 for (auto En : enumerate(ReductionInfos)) {
4722 unsigned Index = En.index();
4723 const ReductionInfo &RI = En.value();
4724 Value *LHSFixupPtr, *RHSFixupPtr;
4725 Builder.restoreIP(RI.ReductionGenClang(
4726 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4727
4728 // Fix the CallBack code genereated to use the correct Values for the LHS
4729 // and RHS
4730 LHSFixupPtr->replaceUsesWithIf(
4731 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4732 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4733 ReductionFunc;
4734 });
4735 RHSFixupPtr->replaceUsesWithIf(
4736 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4737 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4738 ReductionFunc;
4739 });
4740 }
4741
4742 Builder.CreateRetVoid();
4743 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4744 // to the entry block (this is dones for higher opt levels by later passes in
4745 // the pipeline). This has caused issues because non-entry `alloca`s force the
4746 // function to use dynamic stack allocations and we might run out of scratch
4747 // memory.
4748 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4749
4750 return ReductionFunc;
4751}
4752
4753static void
4755 bool IsGPU) {
4756 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4757 (void)RI;
4758 assert(RI.Variable && "expected non-null variable");
4759 assert(RI.PrivateVariable && "expected non-null private variable");
4760 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4761 "expected non-null reduction generator callback");
4762 if (!IsGPU) {
4763 assert(
4764 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4765 "expected variables and their private equivalents to have the same "
4766 "type");
4767 }
4768 assert(RI.Variable->getType()->isPointerTy() &&
4769 "expected variables to be pointers");
4770 }
4771}
4772
4773// The atomic cross-team reduction fast path applies when every reduction in the
4774// set can be represented by an atomicrmw. Clang only populates it for scalar
4775// reductions with a supported atomic operator.
4778 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4779 return static_cast<bool>(RI.AtomicReductionGen);
4780 });
4781}
4782
4784 const LocationDescription &Loc, InsertPointTy AllocaIP,
4785 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4786 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4787 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4788 Value *SrcLocInfo) {
4789 if (!updateToLocation(Loc))
4790 return InsertPointTy();
4791 Builder.restoreIP(CodeGenIP);
4792 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4793 LLVMContext &Ctx = M.getContext();
4794
4795 // Source location for the ident struct
4796 if (!SrcLocInfo) {
4797 uint32_t SrcLocStrSize;
4798 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4799 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4800 }
4801
4802 if (ReductionInfos.size() == 0)
4803 return Builder.saveIP();
4804
4805 BasicBlock *ContinuationBlock = nullptr;
4807 // Copied code from createReductions
4808 BasicBlock *InsertBlock = Loc.IP.getBlock();
4809 ContinuationBlock =
4810 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
4811 InsertBlock->getTerminator()->eraseFromParent();
4812 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
4813 }
4814
4815 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4816 AttributeList FuncAttrs;
4817 AttrBuilder AttrBldr(Ctx);
4818 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4819 AttrBldr.addAttribute(Attr);
4820 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4821 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4822
4823 CodeGenIP = Builder.saveIP();
4824 Expected<Function *> ReductionResult = createReductionFunction(
4825 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4826 ReductionGenCBKind, FuncAttrs);
4827 if (!ReductionResult)
4828 return ReductionResult.takeError();
4829 Function *ReductionFunc = *ReductionResult;
4830 Builder.restoreIP(CodeGenIP);
4831
4832 // Set the grid value in the config needed for lowering later on
4833 if (GridValue.has_value())
4834 Config.setGridValue(GridValue.value());
4835 else
4836 Config.setGridValue(getGridValue(T, ReductionFunc));
4837
4838 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4839 // RedList, shuffle_reduce_func, interwarp_copy_func);
4840 // or
4841 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4842 Value *Res;
4843
4844 // 1. Build a list of reduction variables.
4845 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4846 auto Size = ReductionInfos.size();
4847 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4848 Type *FuncPtrTy =
4849 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4850 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4851 CodeGenIP = Builder.saveIP();
4852 Builder.restoreIP(AllocaIP);
4853 Value *ReductionListAlloca =
4854 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4855 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4856 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4857 Builder.restoreIP(CodeGenIP);
4858 Type *IndexTy = Builder.getIndexTy(
4859 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4860 for (auto En : enumerate(ReductionInfos)) {
4861 const ReductionInfo &RI = En.value();
4862 Value *ElemPtr = Builder.CreateInBoundsGEP(
4863 RedArrayTy, ReductionList,
4864 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4865
4866 Value *PrivateVar = RI.PrivateVariable;
4867 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4868 if (IsByRefElem)
4869 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4870
4871 Value *CastElem =
4872 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4873 Builder.CreateStore(CastElem, ElemPtr);
4874 }
4875 CodeGenIP = Builder.saveIP();
4876 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4877 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4878
4879 if (!SarFunc)
4880 return SarFunc.takeError();
4881
4882 Expected<Function *> CopyResult =
4883 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4884 if (!CopyResult)
4885 return CopyResult.takeError();
4886 Function *WcFunc = *CopyResult;
4887 Builder.restoreIP(CodeGenIP);
4888
4889 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4890
4891 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4892 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4893 // not currently use it. It is computed here conservatively as max(element
4894 // sizes) * N rather than the exact sum, which over-calculates the size for
4895 // mixed reduction types but is harmless given the argument is unused.
4896 // TODO: Consider dropping this computation if the runtime API is ever revised
4897 // to remove the unused parameter.
4898 unsigned MaxDataSize = 0;
4899 SmallVector<Type *> ReductionTypeArgs;
4900 for (auto En : enumerate(ReductionInfos)) {
4901 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4902 // the actual data size stored in the global reduction buffer, consistent
4903 // with the ReductionsBufferTy struct used for GEP offsets below.
4904 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4905 ? En.value().ByRefElementType
4906 : En.value().ElementType;
4907 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4908 if (Size > MaxDataSize)
4909 MaxDataSize = Size;
4910 ReductionTypeArgs.emplace_back(RedTypeArg);
4911 }
4912 Value *ReductionDataSize =
4913 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4914
4915 // Helper function to copy thread-local data back to the original reduction
4916 // list.
4917 Function *CopyScratchToListFunc = nullptr;
4918 // Thread-local storage for the reduction variables.
4919 Value *ScratchForCopyBack = nullptr;
4920 // RL pointer to which the final value from the per-thread scratch should be
4921 // copied back. (Basically RL, appropriately casted if necessary.)
4922 Value *RLForCopyBack = RL;
4923
4924 bool IsAtomicReduction =
4925 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4926
4927 if (!IsTeamsReduction) {
4928 Value *SarFuncCast =
4929 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4930 Value *WcFuncCast =
4931 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4932 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4933 WcFuncCast};
4935 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4936 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4937 } else if (IsAtomicReduction) {
4938 // Atomic cross-team reduction fast path: determine the team's main thread
4939 // that is later to fold its value atomically into the mapped variable.
4940 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4941 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4942 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4943 } else {
4944 CodeGenIP = Builder.saveIP();
4945 StructType *ReductionsBufferTy = StructType::create(
4946 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4947
4948 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4949 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4950 if (!LtGCFunc)
4951 return LtGCFunc.takeError();
4952
4953 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
4954 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4955 if (!GtLCFunc)
4956 return GtLCFunc.takeError();
4957
4958 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
4959 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
4960 if (!GtLRFunc)
4961 return GtLRFunc.takeError();
4962
4963 Builder.restoreIP(CodeGenIP);
4964
4965 // The runtime's cross-team final aggregate uses the storage pointed at by
4966 // its reduce-list argument as per-thread scratch. When the surrounding
4967 // kernel is already in SPMD execution mode, clang emitted each reduction
4968 // private as a per-thread `alloca addrspace(5)`, so the original red_list
4969 // (RL) is already per-thread and nothing else is needed.
4970 //
4971 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
4972 // Generic-mode globalization put the reduction private into team-shared
4973 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
4974 // point all threads of the last team would race on the shared LDS slot.
4975 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
4976 // value in, and hand the per-thread RL to the runtime instead. The writer
4977 // thread copies the final value from that per-thread scratch back to RL
4978 // before running the existing combine path below.
4979
4980 // Thread-local RL (might need localization below before being passed to the
4981 // runtime).
4982 Value *RuntimeRL = RL;
4983
4984 if (!IsSPMD) {
4985 CodeGenIP = Builder.saveIP();
4986 Builder.restoreIP(AllocaIP);
4987 // Allocate thread-local buffer for the reduction variables.
4988 Value *PerThreadScratchAlloca = Builder.CreateAlloca(
4989 ReductionsBufferTy, /*ArraySize=*/nullptr, ".omp.reduction.scratch");
4990 Value *PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
4991 PerThreadScratchAlloca, PtrTy,
4992 PerThreadScratchAlloca->getName() + ".ascast");
4993 // Allocate thread-local buffer for the pointers to the reduction
4994 // variables.
4995 Value *PerThreadRedListAlloca =
4996 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
4997 ".omp.reduction.per_thread_red_list");
4998 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
4999 PerThreadRedListAlloca, PtrTy,
5000 PerThreadRedListAlloca->getName() + ".ascast");
5001 Builder.restoreIP(CodeGenIP);
5002
5003 // Iterate over the reduction variables and copy the team-local value to
5004 // the thread-local buffer.
5005 for (auto En : enumerate(ReductionInfos)) {
5006 const ReductionInfo &RI = En.value();
5007 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5008
5009 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5010 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5011 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5012 0, En.index());
5013
5014 Value *RuntimeListEntry = FieldPtr;
5015 if (IsByRefElem && RI.DataPtrPtrGen) {
5016 Value *SrcDescriptor =
5017 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5018 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5019 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5020 if (!Descriptor)
5021 return Descriptor.takeError();
5022 RuntimeListEntry = *Descriptor;
5023 }
5024 Builder.CreateStore(RuntimeListEntry, Slot);
5025 }
5026 // The copy helpers were emitted with default-AS (AS 0) pointer params
5027 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5028 // but PerThreadScratch and RL live in the target's default AS, which
5029 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5030 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5031 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5032 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5033 PerThreadScratch, CopyArg0Ty);
5034 RLForCopyBack =
5035 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5036 // Use index 0 because there is no array of target values to index into,
5037 // there is only one thread-local memory slot.
5038 // restoreIP above left a stale/empty debug location; this inlinable call
5039 // to a debug-info-bearing helper needs one or the verifier rejects the
5040 // module ("!dbg attachment points at wrong subprogram") after inlining.
5041 Builder.SetCurrentDebugLocation(Loc.DL);
5042 Builder.CreateCall(
5043 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5044 CopyScratchToListFunc = *GtLCFunc;
5045 }
5046
5047 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5048 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5049
5050 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5051 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5052 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5053 }
5054
5055 // 5. Build if (res == 1)
5056 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5057 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5058 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5059 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5060
5061 // 6. Build then branch: where we have reduced values in the master
5062 // thread in each team.
5063 // __kmpc_end_reduce{_nowait}(<gtid>);
5064 // break;
5065 emitBlock(ThenBB, CurFunc);
5066
5067 // Copy the writer thread's per-thread scratch result back into the original
5068 // red-list storage before the existing combine path reads RI.PrivateVariable.
5069 // Set a debug location: this inlinable call to a debug-info-bearing helper
5070 // needs one or the verifier rejects the module after inlining.
5071 if (ScratchForCopyBack) {
5072 Builder.SetCurrentDebugLocation(Loc.DL);
5073 Builder.CreateCall(
5074 CopyScratchToListFunc,
5075 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5076 }
5077
5078 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5079 for (auto En : enumerate(ReductionInfos)) {
5080 const ReductionInfo &RI = En.value();
5081
5082 // Atomic cross-team fast path: each team's main thread folds its
5083 // team-reduced value directly into the mapped reduction variable with a
5084 // single atomicrmw.
5085 if (IsAtomicReduction) {
5087 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5088 if (!AfterIP)
5089 return AfterIP.takeError();
5090 Builder.restoreIP(*AfterIP);
5091 continue;
5092 }
5093
5095 Value *RedValue = RI.Variable;
5096
5097 Value *RHS =
5098 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5099
5101 Value *LHSPtr, *RHSPtr;
5102 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5103 &LHSPtr, &RHSPtr, CurFunc));
5104
5105 // Fix the CallBack code genereated to use the correct Values for the LHS
5106 // and RHS. Cast to match types before replacing (necessary to handle
5107 // different address spaces).
5108 if (LHSPtr->getType() != RedValue->getType())
5109 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5110 RedValue, LHSPtr->getType());
5111 if (RHSPtr->getType() != RHS->getType())
5112 RHS =
5113 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5114
5115 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5116 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5117 ReductionFunc;
5118 });
5119 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5120 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5121 ReductionFunc;
5122 });
5123 } else {
5124 if (IsByRef.empty() || !IsByRef[En.index()]) {
5125 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5126 "red.value." + Twine(En.index()));
5127 }
5128 Value *PrivateRedValue = Builder.CreateLoad(
5129 ValueType, RHS, "red.private.value" + Twine(En.index()));
5130 Value *Reduced;
5131 InsertPointOrErrorTy AfterIP =
5132 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5133 if (!AfterIP)
5134 return AfterIP.takeError();
5135 Builder.restoreIP(*AfterIP);
5136
5137 if (!IsByRef.empty() && !IsByRef[En.index()])
5138 Builder.CreateStore(Reduced, RI.Variable);
5139 }
5140 }
5141 emitBlock(ExitBB, CurFunc);
5142 if (ContinuationBlock) {
5143 Builder.CreateBr(ContinuationBlock);
5144 Builder.SetInsertPoint(ContinuationBlock);
5145 }
5146 Config.setEmitLLVMUsed();
5147
5148 return Builder.saveIP();
5149}
5150
5152 Type *VoidTy = Type::getVoidTy(M.getContext());
5153 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5154 auto *FuncTy =
5155 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5157 ".omp.reduction.func", &M);
5158}
5159
5161 Function *ReductionFunc,
5163 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5164 IRBuilder<>::InsertPointGuard IPG(Builder);
5165 Module *Module = ReductionFunc->getParent();
5166 BasicBlock *ReductionFuncBlock =
5167 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5168 Builder.SetInsertPoint(ReductionFuncBlock);
5169 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5170 Value *LHSArrayPtr = nullptr;
5171 Value *RHSArrayPtr = nullptr;
5172 if (IsGPU) {
5173 // Need to alloca memory here and deal with the pointers before getting
5174 // LHS/RHS pointers out
5175 //
5176 Argument *Arg0 = ReductionFunc->getArg(0);
5177 Argument *Arg1 = ReductionFunc->getArg(1);
5178 Type *Arg0Type = Arg0->getType();
5179 Type *Arg1Type = Arg1->getType();
5180
5181 Value *LHSAlloca =
5182 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5183 Value *RHSAlloca =
5184 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5185 Value *LHSAddrCast =
5186 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5187 Value *RHSAddrCast =
5188 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5189 Builder.CreateStore(Arg0, LHSAddrCast);
5190 Builder.CreateStore(Arg1, RHSAddrCast);
5191 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5192 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5193 } else {
5194 LHSArrayPtr = ReductionFunc->getArg(0);
5195 RHSArrayPtr = ReductionFunc->getArg(1);
5196 }
5197
5198 unsigned NumReductions = ReductionInfos.size();
5199 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5200
5201 for (auto En : enumerate(ReductionInfos)) {
5202 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5203 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5204 RedArrayTy, LHSArrayPtr, 0, En.index());
5205 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5206 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5207 LHSI8Ptr, RI.Variable->getType());
5208 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5209 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5210 RedArrayTy, RHSArrayPtr, 0, En.index());
5211 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5212 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5213 RHSI8Ptr, RI.PrivateVariable->getType());
5214 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5215 Value *Reduced;
5217 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5218 if (!AfterIP)
5219 return AfterIP.takeError();
5220
5221 Builder.restoreIP(*AfterIP);
5222 // TODO: Consider flagging an error.
5223 if (!Builder.GetInsertBlock())
5224 return Error::success();
5225
5226 // store is inside of the reduction region when using by-ref
5227 if (!IsByRef[En.index()])
5228 Builder.CreateStore(Reduced, LHSPtr);
5229 }
5230 Builder.CreateRetVoid();
5231 return Error::success();
5232}
5233
5235 const LocationDescription &Loc, InsertPointTy AllocaIP,
5236 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5237 bool IsNoWait, bool IsTeamsReduction) {
5238 assert(ReductionInfos.size() == IsByRef.size());
5239 if (Config.isGPU())
5240 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5241 IsByRef, IsNoWait, IsTeamsReduction);
5242
5243 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5244
5245 if (!updateToLocation(Loc))
5246 return InsertPointTy();
5247
5248 if (ReductionInfos.size() == 0)
5249 return Builder.saveIP();
5250
5251 BasicBlock *InsertBlock = Loc.IP.getBlock();
5252 BasicBlock *ContinuationBlock =
5253 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
5254 InsertBlock->getTerminator()->eraseFromParent();
5255
5256 // Create and populate array of type-erased pointers to private reduction
5257 // values.
5258 unsigned NumReductions = ReductionInfos.size();
5259 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5260 Builder.SetInsertPoint(AllocaIP.getBlock()->getTerminator());
5261 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5262
5263 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
5264
5265 for (auto En : enumerate(ReductionInfos)) {
5266 unsigned Index = En.index();
5267 const ReductionInfo &RI = En.value();
5268 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5269 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5270 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5271 }
5272
5273 // Emit a call to the runtime function that orchestrates the reduction.
5274 // Declare the reduction function in the process.
5275 Type *IndexTy = Builder.getIndexTy(
5276 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5277 Function *Func = Builder.GetInsertBlock()->getParent();
5278 Module *Module = Func->getParent();
5279 uint32_t SrcLocStrSize;
5280 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5281 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5282 return RI.AtomicReductionGen;
5283 });
5284 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5285 CanGenerateAtomic
5286 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5287 : IdentFlag(0));
5288 Value *ThreadId = getOrCreateThreadID(Ident);
5289 Constant *NumVariables = Builder.getInt32(NumReductions);
5290 const DataLayout &DL = Module->getDataLayout();
5291 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5292 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5293 Function *ReductionFunc = getFreshReductionFunc(*Module);
5294 Value *Lock = getOMPCriticalRegionLock(".reduction");
5296 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5297 : RuntimeFunction::OMPRTL___kmpc_reduce);
5298 CallInst *ReduceCall =
5299 createRuntimeFunctionCall(ReduceFunc,
5300 {Ident, ThreadId, NumVariables, RedArraySize,
5301 RedArray, ReductionFunc, Lock},
5302 "reduce");
5303
5304 // Create final reduction entry blocks for the atomic and non-atomic case.
5305 // Emit IR that dispatches control flow to one of the blocks based on the
5306 // reduction supporting the atomic mode.
5307 BasicBlock *NonAtomicRedBlock =
5308 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5309 BasicBlock *AtomicRedBlock =
5310 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5311 SwitchInst *Switch =
5312 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5313 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5314 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5315
5316 // Populate the non-atomic reduction using the elementwise reduction function.
5317 // This loads the elements from the global and private variables and reduces
5318 // them before storing back the result to the global variable.
5319 Builder.SetInsertPoint(NonAtomicRedBlock);
5320 for (auto En : enumerate(ReductionInfos)) {
5321 const ReductionInfo &RI = En.value();
5323 // We have one less load for by-ref case because that load is now inside of
5324 // the reduction region
5325 Value *RedValue = RI.Variable;
5326 if (!IsByRef[En.index()]) {
5327 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5328 "red.value." + Twine(En.index()));
5329 }
5330 Value *PrivateRedValue =
5331 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5332 "red.private.value." + Twine(En.index()));
5333 Value *Reduced;
5334 InsertPointOrErrorTy AfterIP =
5335 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5336 if (!AfterIP)
5337 return AfterIP.takeError();
5338 Builder.restoreIP(*AfterIP);
5339
5340 if (!Builder.GetInsertBlock())
5341 return InsertPointTy();
5342 // for by-ref case, the load is inside of the reduction region
5343 if (!IsByRef[En.index()])
5344 Builder.CreateStore(Reduced, RI.Variable);
5345 }
5346 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5347 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5348 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5349 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5350 Builder.CreateBr(ContinuationBlock);
5351
5352 // Populate the atomic reduction using the atomic elementwise reduction
5353 // function. There are no loads/stores here because they will be happening
5354 // inside the atomic elementwise reduction.
5355 Builder.SetInsertPoint(AtomicRedBlock);
5356 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5357 for (const ReductionInfo &RI : ReductionInfos) {
5359 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5360 if (!AfterIP)
5361 return AfterIP.takeError();
5362 Builder.restoreIP(*AfterIP);
5363 if (!Builder.GetInsertBlock())
5364 return InsertPointTy();
5365 }
5366 Builder.CreateBr(ContinuationBlock);
5367 } else {
5368 Builder.CreateUnreachable();
5369 }
5370
5371 // Populate the outlined reduction function using the elementwise reduction
5372 // function. Partial values are extracted from the type-erased array of
5373 // pointers to private variables.
5374 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5375 IsByRef, /*isGPU=*/false);
5376 if (Err)
5377 return Err;
5378
5379 if (!Builder.GetInsertBlock())
5380 return InsertPointTy();
5381
5382 Builder.SetInsertPoint(ContinuationBlock);
5383 return Builder.saveIP();
5384}
5385
5388 BodyGenCallbackTy BodyGenCB,
5389 FinalizeCallbackTy FiniCB) {
5390 if (!updateToLocation(Loc))
5391 return Loc.IP;
5392
5393 Directive OMPD = Directive::OMPD_master;
5394 uint32_t SrcLocStrSize;
5395 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5396 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5397 Value *ThreadId = getOrCreateThreadID(Ident);
5398 Value *Args[] = {Ident, ThreadId};
5399
5400 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5401 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5402
5403 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5404 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5405
5406 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5407 /*Conditional*/ true, /*hasFinalize*/ true);
5408}
5409
5412 BodyGenCallbackTy BodyGenCB,
5413 FinalizeCallbackTy FiniCB, Value *Filter) {
5414 if (!updateToLocation(Loc))
5415 return Loc.IP;
5416
5417 Directive OMPD = Directive::OMPD_masked;
5418 uint32_t SrcLocStrSize;
5419 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5420 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5421 Value *ThreadId = getOrCreateThreadID(Ident);
5422 Value *Args[] = {Ident, ThreadId, Filter};
5423 Value *ArgsEnd[] = {Ident, ThreadId};
5424
5425 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5426 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5427
5428 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5429 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5430
5431 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5432 /*Conditional*/ true, /*hasFinalize*/ true);
5433}
5434
5436 llvm::FunctionCallee Callee,
5438 const llvm::Twine &Name) {
5439 llvm::CallInst *Call = Builder.CreateCall(
5440 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5441 Call->setDoesNotThrow();
5442 return Call;
5443}
5444
5445// Expects input basic block is dominated by BeforeScanBB.
5446// Once Scan directive is encountered, the code after scan directive should be
5447// dominated by AfterScanBB. Scan directive splits the code sequence to
5448// scan and input phase. Based on whether inclusive or exclusive
5449// clause is used in the scan directive and whether input loop or scan loop
5450// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5451// input loop and second is the scan loop. The code generated handles only
5452// inclusive scans now.
5454 const LocationDescription &Loc, InsertPointTy AllocaIP,
5455 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5456 bool IsInclusive, ScanInfo *ScanRedInfo) {
5457 if (ScanRedInfo->OMPFirstScanLoop) {
5458 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5459 ScanVarsType, ScanRedInfo);
5460 if (Err)
5461 return Err;
5462 }
5463 if (!updateToLocation(Loc))
5464 return Loc.IP;
5465
5466 llvm::Value *IV = ScanRedInfo->IV;
5467
5468 if (ScanRedInfo->OMPFirstScanLoop) {
5469 // Emit buffer[i] = red; at the end of the input phase.
5470 for (size_t i = 0; i < ScanVars.size(); i++) {
5471 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5472 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5473 Type *DestTy = ScanVarsType[i];
5474 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5475 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5476
5477 Builder.CreateStore(Src, Val);
5478 }
5479 }
5480 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5481 emitBlock(ScanRedInfo->OMPScanDispatch,
5482 Builder.GetInsertBlock()->getParent());
5483
5484 if (!ScanRedInfo->OMPFirstScanLoop) {
5485 IV = ScanRedInfo->IV;
5486 // Emit red = buffer[i]; at the entrance to the scan phase.
5487 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5488 for (size_t i = 0; i < ScanVars.size(); i++) {
5489 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5490 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5491 Type *DestTy = ScanVarsType[i];
5492 Value *SrcPtr =
5493 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5494 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5495 Builder.CreateStore(Src, ScanVars[i]);
5496 }
5497 }
5498
5499 // TODO: Update it to CreateBr and remove dead blocks
5500 llvm::Value *CmpI = Builder.getInt1(true);
5501 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5502 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5503 ScanRedInfo->OMPAfterScanBlock);
5504 } else {
5505 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5506 ScanRedInfo->OMPBeforeScanBlock);
5507 }
5508 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5509 Builder.GetInsertBlock()->getParent());
5510 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5511 return Builder.saveIP();
5512}
5513
5514Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5515 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5516 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5517
5518 Builder.restoreIP(AllocaIP);
5519 // Create the shared pointer at alloca IP.
5520 for (size_t i = 0; i < ScanVars.size(); i++) {
5521 llvm::Value *BuffPtr =
5522 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5523 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5524 }
5525
5526 // Allocate temporary buffer by master thread
5527 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5528 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5529 Builder.restoreIP(CodeGenIP);
5530 Value *AllocSpan =
5531 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5532 for (size_t i = 0; i < ScanVars.size(); i++) {
5533 Type *IntPtrTy = Builder.getInt32Ty();
5534 Constant *Allocsize = ConstantExpr::getSizeOf(ScanVarsType[i]);
5535 Allocsize = ConstantExpr::getTruncOrBitCast(Allocsize, IntPtrTy);
5536 Value *Buff = Builder.CreateMalloc(IntPtrTy, ScanVarsType[i], Allocsize,
5537 AllocSpan, nullptr, "arr");
5538 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5539 }
5540 return Error::success();
5541 };
5542 // TODO: Perform finalization actions for variables. This has to be
5543 // called for variables which have destructors/finalizers.
5544 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5545
5546 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5547 llvm::Value *FilterVal = Builder.getInt32(0);
5549 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5550
5551 if (!AfterIP)
5552 return AfterIP.takeError();
5553 Builder.restoreIP(*AfterIP);
5554 BasicBlock *InputBB = Builder.GetInsertBlock();
5555 if (InputBB->hasTerminator())
5556 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
5557 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5558 if (!AfterIP)
5559 return AfterIP.takeError();
5560 Builder.restoreIP(*AfterIP);
5561
5562 return Error::success();
5563}
5564
5565Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5566 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5567 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5568 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5569 Builder.restoreIP(CodeGenIP);
5570 for (ReductionInfo RedInfo : ReductionInfos) {
5571 Value *PrivateVar = RedInfo.PrivateVariable;
5572 Value *OrigVar = RedInfo.Variable;
5573 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5574 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5575
5576 Type *SrcTy = RedInfo.ElementType;
5577 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5578 "arrayOffset");
5579 Value *Src = Builder.CreateLoad(SrcTy, Val);
5580
5581 Builder.CreateStore(Src, OrigVar);
5582 Builder.CreateFree(Buff);
5583 }
5584 return Error::success();
5585 };
5586 // TODO: Perform finalization actions for variables. This has to be
5587 // called for variables which have destructors/finalizers.
5588 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5589
5590 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5591 Builder.SetInsertPoint(TI);
5592 else
5593 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5594
5595 llvm::Value *FilterVal = Builder.getInt32(0);
5597 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5598
5599 if (!AfterIP)
5600 return AfterIP.takeError();
5601 Builder.restoreIP(*AfterIP);
5602 BasicBlock *InputBB = Builder.GetInsertBlock();
5603 if (InputBB->hasTerminator())
5604 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
5605 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5606 if (!AfterIP)
5607 return AfterIP.takeError();
5608 Builder.restoreIP(*AfterIP);
5609 return Error::success();
5610}
5611
5613 const LocationDescription &Loc,
5615 ScanInfo *ScanRedInfo) {
5616
5617 if (!updateToLocation(Loc))
5618 return Loc.IP;
5619 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5620 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5621 Builder.restoreIP(CodeGenIP);
5622 Function *CurFn = Builder.GetInsertBlock()->getParent();
5623 // for (int k = 0; k <= ceil(log2(n)); ++k)
5624 llvm::BasicBlock *LoopBB =
5625 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5626 llvm::BasicBlock *ExitBB =
5627 splitBB(Builder, false, "omp.outer.log.scan.exit");
5629 Builder.GetInsertBlock()->getModule(),
5630 (llvm::Intrinsic::ID)llvm::Intrinsic::log2, Builder.getDoubleTy());
5631 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5632 llvm::Value *Arg =
5633 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5634 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5636 Builder.GetInsertBlock()->getModule(),
5637 (llvm::Intrinsic::ID)llvm::Intrinsic::ceil, Builder.getDoubleTy());
5638 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5639 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5640 llvm::Value *NMin1 = Builder.CreateNUWSub(
5641 ScanRedInfo->Span,
5642 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5643 Builder.SetInsertPoint(InputBB);
5644 Builder.CreateBr(LoopBB);
5645 emitBlock(LoopBB, CurFn);
5646 Builder.SetInsertPoint(LoopBB);
5647
5648 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5649 // size pow2k = 1;
5650 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5651 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5652 InputBB);
5653 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5654 InputBB);
5655 // for (size i = n - 1; i >= 2 ^ k; --i)
5656 // tmp[i] op= tmp[i-pow2k];
5657 llvm::BasicBlock *InnerLoopBB =
5658 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5659 llvm::BasicBlock *InnerExitBB =
5660 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5661 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5662 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5663 emitBlock(InnerLoopBB, CurFn);
5664 Builder.SetInsertPoint(InnerLoopBB);
5665 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5666 IVal->addIncoming(NMin1, LoopBB);
5667 for (ReductionInfo RedInfo : ReductionInfos) {
5668 Value *ReductionVal = RedInfo.PrivateVariable;
5669 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5670 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5671 Type *DestTy = RedInfo.ElementType;
5672 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5673 Value *LHSPtr =
5674 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5675 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5676 Value *RHSPtr =
5677 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5678 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5679 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5680 llvm::Value *Result;
5681 InsertPointOrErrorTy AfterIP =
5682 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5683 if (!AfterIP)
5684 return AfterIP.takeError();
5685 Builder.CreateStore(Result, LHSPtr);
5686 }
5687 llvm::Value *NextIVal = Builder.CreateNUWSub(
5688 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5689 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5690 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5691 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5692 emitBlock(InnerExitBB, CurFn);
5693 llvm::Value *Next = Builder.CreateNUWAdd(
5694 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5695 Counter->addIncoming(Next, Builder.GetInsertBlock());
5696 // pow2k <<= 1;
5697 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5698 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5699 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5700 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5701 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5702 return Error::success();
5703 };
5704
5705 // TODO: Perform finalization actions for variables. This has to be
5706 // called for variables which have destructors/finalizers.
5707 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5708
5709 llvm::Value *FilterVal = Builder.getInt32(0);
5711 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5712
5713 if (!AfterIP)
5714 return AfterIP.takeError();
5715 Builder.restoreIP(*AfterIP);
5716 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5717
5718 if (!AfterIP)
5719 return AfterIP.takeError();
5720 Builder.restoreIP(*AfterIP);
5721 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5722 if (Err)
5723 return Err;
5724
5725 return AfterIP;
5726}
5727
5728Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5729 llvm::function_ref<Error()> InputLoopGen,
5730 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5731 ScanInfo *ScanRedInfo) {
5732
5733 {
5734 // Emit loop with input phase:
5735 // for (i: 0..<num_iters>) {
5736 // <input phase>;
5737 // buffer[i] = red;
5738 // }
5739 ScanRedInfo->OMPFirstScanLoop = true;
5740 Error Err = InputLoopGen();
5741 if (Err)
5742 return Err;
5743 }
5744 {
5745 // Emit loop with scan phase:
5746 // for (i: 0..<num_iters>) {
5747 // red = buffer[i];
5748 // <scan phase>;
5749 // }
5750 ScanRedInfo->OMPFirstScanLoop = false;
5751 Error Err = ScanLoopGen(Builder.saveIP());
5752 if (Err)
5753 return Err;
5754 }
5755 return Error::success();
5756}
5757
5758void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5759 Function *Fun = Builder.GetInsertBlock()->getParent();
5760 ScanRedInfo->OMPScanDispatch =
5761 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5762 ScanRedInfo->OMPAfterScanBlock =
5763 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5764 ScanRedInfo->OMPBeforeScanBlock =
5765 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5766 ScanRedInfo->OMPScanLoopExit =
5767 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5768}
5770 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5771 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5772 Module *M = F->getParent();
5773 LLVMContext &Ctx = M->getContext();
5774 Type *IndVarTy = TripCount->getType();
5775
5776 // Create the basic block structure.
5777 BasicBlock *Preheader =
5778 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5779 BasicBlock *Header =
5780 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5781 BasicBlock *Cond =
5782 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5783 BasicBlock *Body =
5784 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5785 BasicBlock *Latch =
5786 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5787 BasicBlock *Exit =
5788 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5789 BasicBlock *After =
5790 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5791
5792 // Use specified DebugLoc for new instructions.
5793 Builder.SetCurrentDebugLocation(DL);
5794
5795 Builder.SetInsertPoint(Preheader);
5796 Builder.CreateBr(Header);
5797
5798 Builder.SetInsertPoint(Header);
5799 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5800 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5801 Builder.CreateBr(Cond);
5802
5803 Builder.SetInsertPoint(Cond);
5804 Value *Cmp =
5805 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5806 Builder.CreateCondBr(Cmp, Body, Exit);
5807
5808 Builder.SetInsertPoint(Body);
5809 Builder.CreateBr(Latch);
5810
5811 Builder.SetInsertPoint(Latch);
5812 // Decide whether the induction variable increment can carry nsw.
5813 //
5814 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5815 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5816 // for valid programs 0 <= count <= INT_MAX always holds.
5817 //
5818 // Collapsed loops: the trip count is a product that can overflow i32 even for
5819 // a conforming program, so nsw is kept only when the product is a constant
5820 // that provably fits, dropped otherwise.
5821 bool HasNSW = Config.hasNoSignedWrap();
5822 if (HasNSW) {
5823 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5824 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5826 if (CI->getValue().ugt(SignedMax))
5827 HasNSW = false;
5828 } else if (IsCollapsed) {
5829 HasNSW = false;
5830 }
5831 }
5832 Value *Next =
5833 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5834 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5835 Builder.CreateBr(Header);
5836 IndVarPHI->addIncoming(Next, Latch);
5837
5838 Builder.SetInsertPoint(Exit);
5839 Builder.CreateBr(After);
5840
5841 // Remember and return the canonical control flow.
5842 LoopInfos.emplace_front();
5843 CanonicalLoopInfo *CL = &LoopInfos.front();
5844
5845 CL->Header = Header;
5846 CL->Cond = Cond;
5847 CL->Latch = Latch;
5848 CL->Exit = Exit;
5849
5850#ifndef NDEBUG
5851 CL->assertOK();
5852#endif
5853 return CL;
5854}
5855
5858 LoopBodyGenCallbackTy BodyGenCB,
5859 Value *TripCount, const Twine &Name) {
5860 BasicBlock *BB = Loc.IP.getBlock();
5861 BasicBlock *NextBB = BB->getNextNode();
5862
5863 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5864 NextBB, NextBB, Name);
5865 BasicBlock *After = CL->getAfter();
5866
5867 // If location is not set, don't connect the loop.
5868 if (updateToLocation(Loc)) {
5869 // Split the loop at the insertion point: Branch to the preheader and move
5870 // every following instruction to after the loop (the After BB). Also, the
5871 // new successor is the loop's after block.
5872 spliceBB(Builder, After, /*CreateBranch=*/false);
5873 Builder.CreateBr(CL->getPreheader());
5874 }
5875
5876 // Emit the body content. We do it after connecting the loop to the CFG to
5877 // avoid that the callback encounters degenerate BBs.
5878 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5879 return Err;
5880
5881#ifndef NDEBUG
5882 CL->assertOK();
5883#endif
5884 return CL;
5885}
5886
5888 ScanInfos.emplace_front();
5889 ScanInfo *Result = &ScanInfos.front();
5890 return Result;
5891}
5892
5896 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5897 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5898 LocationDescription ComputeLoc =
5899 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5900 updateToLocation(ComputeLoc);
5901
5903
5905 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5906 ScanRedInfo->Span = TripCount;
5907 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5908 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5909
5910 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5911 Builder.restoreIP(CodeGenIP);
5912 ScanRedInfo->IV = IV;
5913 createScanBBs(ScanRedInfo);
5914 BasicBlock *InputBlock = Builder.GetInsertBlock();
5915 Instruction *Terminator = InputBlock->getTerminator();
5916 assert(Terminator->getNumSuccessors() == 1);
5917 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5918 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5919 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5920 Builder.GetInsertBlock()->getParent());
5921 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5922 emitBlock(ScanRedInfo->OMPScanLoopExit,
5923 Builder.GetInsertBlock()->getParent());
5924 Builder.CreateBr(ContinueBlock);
5925 Builder.SetInsertPoint(
5926 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5927 return BodyGenCB(Builder.saveIP(), IV);
5928 };
5929
5930 const auto &&InputLoopGen = [&]() -> Error {
5932 Builder.saveIP(), BodyGen, Start, Stop, Step, IsSigned, InclusiveStop,
5933 ComputeIP, Name, true, ScanRedInfo);
5934 if (!LoopInfo)
5935 return LoopInfo.takeError();
5936 Result.push_back(*LoopInfo);
5937 Builder.restoreIP((*LoopInfo)->getAfterIP());
5938 return Error::success();
5939 };
5940 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5942 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5943 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5944 if (!LoopInfo)
5945 return LoopInfo.takeError();
5946 Result.push_back(*LoopInfo);
5947 Builder.restoreIP((*LoopInfo)->getAfterIP());
5948 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5949 return Error::success();
5950 };
5951 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5952 if (Err)
5953 return Err;
5954 return Result;
5955}
5956
5958 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5959 bool IsSigned, bool InclusiveStop, const Twine &Name) {
5960
5961 // Consider the following difficulties (assuming 8-bit signed integers):
5962 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
5963 // DO I = 1, 100, 50
5964 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
5965 // DO I = 100, 0, -128
5966
5967 // Start, Stop and Step must be of the same integer type.
5968 auto *IndVarTy = cast<IntegerType>(Start->getType());
5969 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
5970 assert(IndVarTy == Step->getType() && "Step type mismatch");
5971
5973
5974 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
5975 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
5976
5977 // Like Step, but always positive.
5978 Value *Incr = Step;
5979
5980 // Distance between Start and Stop; always positive.
5981 Value *Span;
5982
5983 // Condition whether there are no iterations are executed at all, e.g. because
5984 // UB < LB.
5985 Value *ZeroCmp;
5986
5987 if (IsSigned) {
5988 // Ensure that increment is positive. If not, negate and invert LB and UB.
5989 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
5990 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
5991 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
5992 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
5993 Span = Builder.CreateSub(UB, LB, "", false, true);
5994 ZeroCmp = Builder.CreateICmp(
5995 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
5996 } else {
5997 Span = Builder.CreateSub(Stop, Start, "", true);
5998 ZeroCmp = Builder.CreateICmp(
5999 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6000 }
6001
6002 Value *CountIfLooping;
6003 if (InclusiveStop) {
6004 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6005 } else {
6006 // Avoid incrementing past stop since it could overflow.
6007 Value *CountIfTwo = Builder.CreateAdd(
6008 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6009 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6010 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6011 }
6012
6013 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6014 "omp_" + Name + ".tripcount");
6015}
6016
6019 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6020 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6021 ScanInfo *ScanRedInfo) {
6022 LocationDescription ComputeLoc =
6023 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6024
6026 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6027
6028 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6029 Builder.restoreIP(CodeGenIP);
6030 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6031 /*HasNSW=*/Config.hasNoSignedWrap());
6032 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6033 /*HasNSW=*/Config.hasNoSignedWrap());
6034 if (InScan)
6035 ScanRedInfo->IV = IndVar;
6036 return BodyGenCB(Builder.saveIP(), IndVar);
6037 };
6038 LocationDescription LoopLoc =
6039 ComputeIP.isSet()
6040 ? Loc
6041 : LocationDescription(Builder.saveIP(),
6042 Builder.getCurrentDebugLocation());
6043 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6044}
6045
6046// Returns an LLVM function to call for initializing loop bounds using OpenMP
6047// static scheduling for composite `distribute parallel for` depending on
6048// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6049// integers as unsigned similarly to CanonicalLoopInfo.
6050static FunctionCallee
6052 OpenMPIRBuilder &OMPBuilder) {
6053 unsigned Bitwidth = Ty->getIntegerBitWidth();
6054 if (Bitwidth == 32)
6055 return OMPBuilder.getOrCreateRuntimeFunction(
6056 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6057 if (Bitwidth == 64)
6058 return OMPBuilder.getOrCreateRuntimeFunction(
6059 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6060 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6061}
6062
6063// Returns an LLVM function to call for initializing loop bounds using OpenMP
6064// static scheduling depending on `type`. Only i32 and i64 are supported by the
6065// runtime. Always interpret integers as unsigned similarly to
6066// CanonicalLoopInfo.
6068 OpenMPIRBuilder &OMPBuilder) {
6069 unsigned Bitwidth = Ty->getIntegerBitWidth();
6070 if (Bitwidth == 32)
6071 return OMPBuilder.getOrCreateRuntimeFunction(
6072 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6073 if (Bitwidth == 64)
6074 return OMPBuilder.getOrCreateRuntimeFunction(
6075 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6076 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6077}
6078
6079OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6080 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6081 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6082 OMPScheduleType DistScheduleSchedType) {
6083 assert(CLI->isValid() && "Requires a valid canonical loop");
6084 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6085 "Require dedicated allocate IP");
6086
6087 // Set up the source location value for OpenMP runtime.
6088 Builder.restoreIP(CLI->getPreheaderIP());
6089 Builder.SetCurrentDebugLocation(DL);
6090
6091 uint32_t SrcLocStrSize;
6092 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6094 switch (LoopType) {
6095 case WorksharingLoopType::ForStaticLoop:
6096 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6097 break;
6098 case WorksharingLoopType::DistributeStaticLoop:
6099 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6100 break;
6101 case WorksharingLoopType::DistributeForStaticLoop:
6102 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6103 break;
6104 }
6105 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6106
6107 // Declare useful OpenMP runtime functions.
6108 Value *IV = CLI->getIndVar();
6109 Type *IVTy = IV->getType();
6110 FunctionCallee StaticInit =
6111 LoopType == WorksharingLoopType::DistributeForStaticLoop
6112 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6113 : getKmpcForStaticInitForType(IVTy, M, *this);
6114 FunctionCallee StaticFini =
6115 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6116
6117 // Allocate space for computed loop bounds as expected by the "init" function.
6118 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6119
6120 Type *I32Type = Type::getInt32Ty(M.getContext());
6121 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6122 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6123 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6124 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6125 CLI->setLastIter(PLastIter);
6126
6127 // At the end of the preheader, prepare for calling the "init" function by
6128 // storing the current loop bounds into the allocated space. A canonical loop
6129 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6130 // and produces an inclusive upper bound.
6131 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6132 Constant *Zero = ConstantInt::get(IVTy, 0);
6133 Constant *One = ConstantInt::get(IVTy, 1);
6134 Builder.CreateStore(Zero, PLowerBound);
6135 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6136 Builder.CreateStore(UpperBound, PUpperBound);
6137 Builder.CreateStore(One, PStride);
6138
6139 Value *ThreadNum =
6140 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6141
6142 OMPScheduleType SchedType =
6143 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6144 ? OMPScheduleType::OrderedDistribute
6146 Constant *SchedulingType =
6147 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6148
6149 // Call the "init" function and update the trip count of the loop with the
6150 // value it produced.
6151 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6152 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6153 this](Value *SchedulingType, auto &Builder) {
6154 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6155 PLowerBound, PUpperBound});
6156 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6157 Value *PDistUpperBound =
6158 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6159 Args.push_back(PDistUpperBound);
6160 }
6161 Args.append({PStride, One, Zero});
6162 createRuntimeFunctionCall(StaticInit, Args);
6163 };
6164 BuildInitCall(SchedulingType, Builder);
6165 if (HasDistSchedule &&
6166 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6167 Constant *DistScheduleSchedType = ConstantInt::get(
6168 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6169 // We want to emit a second init function call for the dist_schedule clause
6170 // to the Distribute construct. This should only be done however if a
6171 // Workshare Loop is nested within a Distribute Construct
6172 BuildInitCall(DistScheduleSchedType, Builder);
6173 }
6174 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6175 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6176 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6177 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6178 CLI->setTripCount(TripCount);
6179
6180 // Update all uses of the induction variable except the one in the condition
6181 // block that compares it with the actual upper bound, and the increment in
6182 // the latch block.
6183
6184 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6185 Builder.SetInsertPoint(CLI->getBody(),
6186 CLI->getBody()->getFirstInsertionPt());
6187 Builder.SetCurrentDebugLocation(DL);
6188 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6189 /*HasNSW=*/Config.hasNoSignedWrap());
6190 });
6191
6192 // In the "exit" block, call the "fini" function.
6193 Builder.SetInsertPoint(CLI->getExit(),
6194 CLI->getExit()->getTerminator()->getIterator());
6195 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6196
6197 // Add the barrier if requested.
6198 if (NeedsBarrier) {
6199 InsertPointOrErrorTy BarrierIP =
6201 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6202 /* CheckCancelFlag */ false);
6203 if (!BarrierIP)
6204 return BarrierIP.takeError();
6205 }
6206
6207 InsertPointTy AfterIP = CLI->getAfterIP();
6208 CLI->invalidate();
6209
6210 return AfterIP;
6211}
6212
6213static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6214 LoopInfo &LI);
6215static void addLoopMetadata(CanonicalLoopInfo *Loop,
6216 ArrayRef<Metadata *> Properties);
6217
6219 LLVMContext &Ctx, Loop *Loop,
6221 SmallVector<Metadata *> &LoopMDList) {
6222 SmallSet<BasicBlock *, 8> Reachable;
6223
6224 // Get the basic blocks from the loop in which memref instructions
6225 // can be found.
6226 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6227 // preferably without running any passes.
6228 for (BasicBlock *Block : Loop->getBlocks()) {
6229 if (Block == CLI->getCond() || Block == CLI->getHeader())
6230 continue;
6231 Reachable.insert(Block);
6232 }
6233
6234 // Add access group metadata to memory-access instructions.
6235 MDNode *AccessGroup = MDNode::getDistinct(Ctx, {});
6236 for (BasicBlock *BB : Reachable)
6237 addAccessGroupMetadata(BB, AccessGroup, LoopInfo);
6238 // TODO: If the loop has existing parallel access metadata, have
6239 // to combine two lists.
6240 LoopMDList.push_back(MDNode::get(
6241 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6242}
6243
6245OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6246 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6247 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6248 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6249 assert(CLI->isValid() && "Requires a valid canonical loop");
6250 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6251
6252 LLVMContext &Ctx = CLI->getFunction()->getContext();
6253 Value *IV = CLI->getIndVar();
6254 Value *OrigTripCount = CLI->getTripCount();
6255 Type *IVTy = IV->getType();
6256 assert(IVTy->getIntegerBitWidth() <= 64 &&
6257 "Max supported tripcount bitwidth is 64 bits");
6258 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6259 : Type::getInt64Ty(Ctx);
6260 Type *I32Type = Type::getInt32Ty(M.getContext());
6261 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6262 Constant *One = ConstantInt::get(InternalIVTy, 1);
6263
6264 Function *F = CLI->getFunction();
6265 // Blocks must have terminators.
6266 // FIXME: Don't run analyses on incomplete/invalid IR.
6267 SmallVector<Instruction *> UIs;
6268 for (BasicBlock &BB : *F)
6269 if (!BB.hasTerminator())
6270 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6272 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6273 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6274 LoopAnalysis LIA;
6275 LoopInfo &&LI = LIA.run(*F, FAM);
6276 for (Instruction *I : UIs)
6277 I->eraseFromParent();
6278 Loop *L = LI.getLoopFor(CLI->getHeader());
6279 SmallVector<Metadata *> LoopMDList;
6280 if (ChunkSize || DistScheduleChunkSize)
6281 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6282 addLoopMetadata(CLI, LoopMDList);
6283
6284 // Declare useful OpenMP runtime functions.
6285 FunctionCallee StaticInit =
6286 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6287 FunctionCallee StaticFini =
6288 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6289
6290 // Allocate space for computed loop bounds as expected by the "init" function.
6291 Builder.restoreIP(AllocaIP);
6292 Builder.SetCurrentDebugLocation(DL);
6293 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6294 Value *PLowerBound =
6295 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6296 Value *PUpperBound =
6297 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6298 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6299 CLI->setLastIter(PLastIter);
6300
6301 // Set up the source location value for the OpenMP runtime.
6302 Builder.restoreIP(CLI->getPreheaderIP());
6303 Builder.SetCurrentDebugLocation(DL);
6304
6305 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6306 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6307 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6308 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6309 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6310 "distschedulechunksize");
6311 Value *CastedTripCount =
6312 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6313
6314 Constant *SchedulingType =
6315 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6316 Constant *DistSchedulingType =
6317 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6318 Builder.CreateStore(Zero, PLowerBound);
6319 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6320 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6321 Value *UpperBound =
6322 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6323 Builder.CreateStore(UpperBound, PUpperBound);
6324 Builder.CreateStore(One, PStride);
6325
6326 // Call the "init" function and update the trip count of the loop with the
6327 // value it produced.
6328 uint32_t SrcLocStrSize;
6329 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6330 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6331 if (DistScheduleSchedType != OMPScheduleType::None) {
6332 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6333 }
6334 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6335 Value *ThreadNum =
6336 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6337 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6338 PUpperBound, PStride, One,
6339 this](Value *SchedulingType, Value *ChunkSize,
6340 auto &Builder) {
6342 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6343 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6344 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6345 /*pstride=*/PStride, /*incr=*/One,
6346 /*chunk=*/ChunkSize});
6347 };
6348 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6349 if (DistScheduleSchedType != OMPScheduleType::None &&
6350 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6351 SchedType != OMPScheduleType::OrderedDistribute) {
6352 // We want to emit a second init function call for the dist_schedule clause
6353 // to the Distribute construct. This should only be done however if a
6354 // Workshare Loop is nested within a Distribute Construct
6355 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6356 }
6357
6358 // Load values written by the "init" function.
6359 Value *FirstChunkStart =
6360 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6361 Value *FirstChunkStop =
6362 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6363 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6364 Value *ChunkRange =
6365 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6366 Value *NextChunkStride =
6367 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6368
6369 // Create outer "dispatch" loop for enumerating the chunks.
6370 BasicBlock *DispatchEnter = splitBB(Builder, true);
6371 Value *DispatchCounter;
6372
6373 // It is safe to assume this didn't return an error because the callback
6374 // passed into createCanonicalLoop is the only possible error source, and it
6375 // always returns success.
6376 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6377 {Builder.saveIP(), DL},
6378 [&](InsertPointTy BodyIP, Value *Counter) {
6379 DispatchCounter = Counter;
6380 return Error::success();
6381 },
6382 FirstChunkStart, CastedTripCount, NextChunkStride,
6383 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6384 "dispatch"));
6385
6386 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6387 // not have to preserve the canonical invariant.
6388 BasicBlock *DispatchBody = DispatchCLI->getBody();
6389 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6390 BasicBlock *DispatchExit = DispatchCLI->getExit();
6391 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6392 DispatchCLI->invalidate();
6393
6394 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6395 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6396 redirectTo(CLI->getExit(), DispatchLatch, DL);
6397 redirectTo(DispatchBody, DispatchEnter, DL);
6398
6399 // Prepare the prolog of the chunk loop.
6400 Builder.restoreIP(CLI->getPreheaderIP());
6401 Builder.SetCurrentDebugLocation(DL);
6402
6403 // Compute the number of iterations of the chunk loop.
6404 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6405 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6406 Value *IsLastChunk =
6407 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6408 Value *CountUntilOrigTripCount =
6409 Builder.CreateSub(CastedTripCount, DispatchCounter);
6410 Value *ChunkTripCount = Builder.CreateSelect(
6411 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6412 Value *BackcastedChunkTC =
6413 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6414 CLI->setTripCount(BackcastedChunkTC);
6415
6416 // Update all uses of the induction variable except the one in the condition
6417 // block that compares it with the actual upper bound, and the increment in
6418 // the latch block.
6419 Value *BackcastedDispatchCounter =
6420 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6421 CLI->mapIndVar([&](Instruction *) -> Value * {
6422 Builder.restoreIP(CLI->getBodyIP());
6423 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6424 });
6425
6426 // In the "exit" block, call the "fini" function.
6427 Builder.SetInsertPoint(DispatchExit, DispatchExit->getFirstInsertionPt());
6428 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6429
6430 // Add the barrier if requested.
6431 if (NeedsBarrier) {
6432 InsertPointOrErrorTy AfterIP =
6433 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6434 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6435 if (!AfterIP)
6436 return AfterIP.takeError();
6437 }
6438
6439#ifndef NDEBUG
6440 // Even though we currently do not support applying additional methods to it,
6441 // the chunk loop should remain a canonical loop.
6442 CLI->assertOK();
6443#endif
6444
6445 return InsertPointTy(DispatchAfter, DispatchAfter->getFirstInsertionPt());
6446}
6447
6448// Returns an LLVM function to call for executing an OpenMP static worksharing
6449// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6450// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6451static FunctionCallee
6453 WorksharingLoopType LoopType) {
6454 unsigned Bitwidth = Ty->getIntegerBitWidth();
6455 Module &M = OMPBuilder->M;
6456 switch (LoopType) {
6457 case WorksharingLoopType::ForStaticLoop:
6458 if (Bitwidth == 32)
6459 return OMPBuilder->getOrCreateRuntimeFunction(
6460 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6461 if (Bitwidth == 64)
6462 return OMPBuilder->getOrCreateRuntimeFunction(
6463 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6464 break;
6465 case WorksharingLoopType::DistributeStaticLoop:
6466 if (Bitwidth == 32)
6467 return OMPBuilder->getOrCreateRuntimeFunction(
6468 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6469 if (Bitwidth == 64)
6470 return OMPBuilder->getOrCreateRuntimeFunction(
6471 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6472 break;
6473 case WorksharingLoopType::DistributeForStaticLoop:
6474 if (Bitwidth == 32)
6475 return OMPBuilder->getOrCreateRuntimeFunction(
6476 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6477 if (Bitwidth == 64)
6478 return OMPBuilder->getOrCreateRuntimeFunction(
6479 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6480 break;
6481 }
6482 if (Bitwidth != 32 && Bitwidth != 64) {
6483 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6484 }
6485 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6486}
6487
6488// Inserts a call to proper OpenMP Device RTL function which handles
6489// loop worksharing.
6491 WorksharingLoopType LoopType,
6492 BasicBlock *InsertBlock, Value *Ident,
6493 Value *LoopBodyArg, Value *TripCount,
6494 Function &LoopBodyFn, bool NoLoop) {
6495 Type *TripCountTy = TripCount->getType();
6496 Module &M = OMPBuilder->M;
6497 IRBuilder<> &Builder = OMPBuilder->Builder;
6498 FunctionCallee RTLFn =
6499 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6500 SmallVector<Value *, 8> RealArgs;
6501 RealArgs.push_back(Ident);
6502 RealArgs.push_back(&LoopBodyFn);
6503 RealArgs.push_back(LoopBodyArg);
6504 RealArgs.push_back(TripCount);
6505 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6506 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6507 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6508 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6509 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6510 return;
6511 }
6512 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6513 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6514 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6515 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6516
6517 RealArgs.push_back(
6518 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6519 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6520 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6521 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6522 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6523 } else {
6524 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6525 }
6526
6527 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6528}
6529
6531 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6532 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6533 WorksharingLoopType LoopType, bool NoLoop) {
6534 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6535 BasicBlock *Preheader = CLI->getPreheader();
6536 Value *TripCount = CLI->getTripCount();
6537
6538 // After loop body outling, the loop body contains only set up
6539 // of loop body argument structure and the call to the outlined
6540 // loop body function. Firstly, we need to move setup of loop body args
6541 // into loop preheader.
6542 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6543 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6544
6545 // The next step is to remove the whole loop. We do not it need anymore.
6546 // That's why make an unconditional branch from loop preheader to loop
6547 // exit block
6548 Builder.restoreIP({Preheader, Preheader->end()});
6549 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6550 Preheader->getTerminator()->eraseFromParent();
6551 Builder.CreateBr(CLI->getExit());
6552
6553 // Delete dead loop blocks
6554 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6555 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6556 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6557 CleanUpInfo.EntryBB = CLI->getHeader();
6558 CleanUpInfo.ExitBB = CLI->getExit();
6559 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6560 DeleteDeadBlocks(BlocksToBeRemoved);
6561
6562 // Find the instruction which corresponds to loop body argument structure
6563 // and remove the call to loop body function instruction.
6564 Value *LoopBodyArg;
6565 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6566 assert(OutlinedFnUser &&
6567 "Expected unique undroppable user of outlined function");
6568 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6569 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6570 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6571 "Expected outlined function call to be located in loop preheader");
6572 // Check in case no argument structure has been passed.
6573 if (OutlinedFnCallInstruction->arg_size() > 1)
6574 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6575 else
6576 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6577 OutlinedFnCallInstruction->eraseFromParent();
6578
6579 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6580 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6581
6582 for (auto &ToBeDeletedItem : ToBeDeleted)
6583 ToBeDeletedItem->eraseFromParent();
6584 CLI->invalidate();
6585}
6586
6587OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6588 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6589 WorksharingLoopType LoopType, bool NoLoop) {
6590 uint32_t SrcLocStrSize;
6591 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6593 switch (LoopType) {
6594 case WorksharingLoopType::ForStaticLoop:
6595 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6596 break;
6597 case WorksharingLoopType::DistributeStaticLoop:
6598 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6599 break;
6600 case WorksharingLoopType::DistributeForStaticLoop:
6601 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6602 break;
6603 }
6604 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6605
6606 auto OI = std::make_unique<OutlineInfo>();
6607 OI->OuterAllocBB = CLI->getPreheader();
6608 Function *OuterFn = CLI->getPreheader()->getParent();
6609
6610 // Instructions which need to be deleted at the end of code generation
6611 SmallVector<Instruction *, 4> ToBeDeleted;
6612
6613 OI->OuterAllocBB = AllocaIP.getBlock();
6614
6615 // Mark the body loop as region which needs to be extracted
6616 OI->EntryBB = CLI->getBody();
6617 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6618 "omp.prelatch");
6619
6620 // Prepare loop body for extraction
6621 Builder.restoreIP({CLI->getPreheader(), CLI->getPreheader()->begin()});
6622
6623 // Insert new loop counter variable which will be used only in loop
6624 // body.
6625 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6626 Instruction *NewLoopCntLoad =
6627 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6628 // New loop counter instructions are redundant in the loop preheader when
6629 // code generation for workshare loop is finshed. That's why mark them as
6630 // ready for deletion.
6631 ToBeDeleted.push_back(NewLoopCntLoad);
6632 ToBeDeleted.push_back(NewLoopCnt);
6633
6634 // Analyse loop body region. Find all input variables which are used inside
6635 // loop body region.
6636 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6638 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6639
6640 CodeExtractorAnalysisCache CEAC(*OuterFn);
6641 CodeExtractor Extractor(Blocks,
6642 /* DominatorTree */ nullptr,
6643 /* AggregateArgs */ true,
6644 /* BlockFrequencyInfo */ nullptr,
6645 /* BranchProbabilityInfo */ nullptr,
6646 /* AssumptionCache */ nullptr,
6647 /* AllowVarArgs */ true,
6648 /* AllowAlloca */ true,
6649 /* AllocationBlock */ CLI->getPreheader(),
6650 /* DeallocationBlocks */ {},
6651 /* Suffix */ ".omp_wsloop",
6652 /* AggrArgsIn0AddrSpace */ true);
6653
6654 BasicBlock *CommonExit = nullptr;
6655 SetVector<Value *> SinkingCands, HoistingCands;
6656
6657 // Find allocas outside the loop body region which are used inside loop
6658 // body
6659 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6660
6661 // We need to model loop body region as the function f(cnt, loop_arg).
6662 // That's why we replace loop induction variable by the new counter
6663 // which will be one of loop body function argument
6665 CLI->getIndVar()->user_end());
6666 for (auto Use : Users) {
6667 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6668 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6669 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6670 }
6671 }
6672 }
6673 // Make sure that loop counter variable is not merged into loop body
6674 // function argument structure and it is passed as separate variable
6675 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6676
6677 // PostOutline CB is invoked when loop body function is outlined and
6678 // loop body is replaced by call to outlined function. We need to add
6679 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6680 // function will handle loop control logic.
6681 //
6682 OI->PostOutlineCB = [=, ToBeDeletedVec =
6683 std::move(ToBeDeleted)](Function &OutlinedFn) {
6684 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6685 LoopType, NoLoop);
6686 };
6687 addOutlineInfo(std::move(OI));
6688 return CLI->getAfterIP();
6689}
6690
6693 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6694 bool HasSimdModifier, bool HasMonotonicModifier,
6695 bool HasNonmonotonicModifier, bool HasOrderedClause,
6696 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6697 Value *DistScheduleChunkSize) {
6698 if (Config.isTargetDevice())
6699 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NoLoop);
6700 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6701 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6702 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6703
6704 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6705 OMPScheduleType::ModifierOrdered;
6706 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6707 if (HasDistSchedule) {
6708 DistScheduleSchedType = DistScheduleChunkSize
6709 ? OMPScheduleType::OrderedDistributeChunked
6710 : OMPScheduleType::OrderedDistribute;
6711 }
6712 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6713 case OMPScheduleType::BaseStatic:
6714 case OMPScheduleType::BaseDistribute:
6715 assert((!ChunkSize || !DistScheduleChunkSize) &&
6716 "No chunk size with static-chunked schedule");
6717 if (IsOrdered && !HasDistSchedule)
6718 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6719 NeedsBarrier, ChunkSize);
6720 // FIXME: Monotonicity ignored?
6721 if (DistScheduleChunkSize)
6722 return applyStaticChunkedWorkshareLoop(
6723 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6724 DistScheduleChunkSize, DistScheduleSchedType);
6725 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6726 HasDistSchedule);
6727
6728 case OMPScheduleType::BaseStaticChunked:
6729 case OMPScheduleType::BaseDistributeChunked:
6730 if (IsOrdered && !HasDistSchedule)
6731 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6732 NeedsBarrier, ChunkSize);
6733 // FIXME: Monotonicity ignored?
6734 return applyStaticChunkedWorkshareLoop(
6735 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6736 DistScheduleChunkSize, DistScheduleSchedType);
6737
6738 case OMPScheduleType::BaseRuntime:
6739 case OMPScheduleType::BaseAuto:
6740 case OMPScheduleType::BaseGreedy:
6741 case OMPScheduleType::BaseBalanced:
6742 case OMPScheduleType::BaseSteal:
6743 case OMPScheduleType::BaseRuntimeSimd:
6744 assert(!ChunkSize &&
6745 "schedule type does not support user-defined chunk sizes");
6746 [[fallthrough]];
6747 case OMPScheduleType::BaseGuidedSimd:
6748 case OMPScheduleType::BaseDynamicChunked:
6749 case OMPScheduleType::BaseGuidedChunked:
6750 case OMPScheduleType::BaseGuidedIterativeChunked:
6751 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6752 case OMPScheduleType::BaseStaticBalancedChunked:
6753 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6754 NeedsBarrier, ChunkSize);
6755
6756 default:
6757 llvm_unreachable("Unknown/unimplemented schedule kind");
6758 }
6759}
6760
6761/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6762/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6763/// the runtime. Always interpret integers as unsigned similarly to
6764/// CanonicalLoopInfo.
6765static FunctionCallee
6767 unsigned Bitwidth = Ty->getIntegerBitWidth();
6768 if (Bitwidth == 32)
6769 return OMPBuilder.getOrCreateRuntimeFunction(
6770 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6771 if (Bitwidth == 64)
6772 return OMPBuilder.getOrCreateRuntimeFunction(
6773 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6774 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6775}
6776
6777/// Returns an LLVM function to call for updating the next loop using OpenMP
6778/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6779/// the runtime. Always interpret integers as unsigned similarly to
6780/// CanonicalLoopInfo.
6781static FunctionCallee
6783 unsigned Bitwidth = Ty->getIntegerBitWidth();
6784 if (Bitwidth == 32)
6785 return OMPBuilder.getOrCreateRuntimeFunction(
6786 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6787 if (Bitwidth == 64)
6788 return OMPBuilder.getOrCreateRuntimeFunction(
6789 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6790 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6791}
6792
6793/// Returns an LLVM function to call for finalizing the dynamic loop using
6794/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6795/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6796static FunctionCallee
6798 unsigned Bitwidth = Ty->getIntegerBitWidth();
6799 if (Bitwidth == 32)
6800 return OMPBuilder.getOrCreateRuntimeFunction(
6801 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6802 if (Bitwidth == 64)
6803 return OMPBuilder.getOrCreateRuntimeFunction(
6804 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6805 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6806}
6807
6809OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6810 InsertPointTy AllocaIP,
6811 OMPScheduleType SchedType,
6812 bool NeedsBarrier, Value *Chunk) {
6813 assert(CLI->isValid() && "Requires a valid canonical loop");
6814 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6815 "Require dedicated allocate IP");
6817 "Require valid schedule type");
6818
6819 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6820 OMPScheduleType::ModifierOrdered;
6821
6822 // Set up the source location value for OpenMP runtime.
6823 Builder.SetCurrentDebugLocation(DL);
6824
6825 uint32_t SrcLocStrSize;
6826 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6827 Value *SrcLoc =
6828 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6829
6830 // Declare useful OpenMP runtime functions.
6831 Value *IV = CLI->getIndVar();
6832 Type *IVTy = IV->getType();
6833 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6834 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6835
6836 // Allocate space for computed loop bounds as expected by the "init" function.
6837 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6838 Type *I32Type = Type::getInt32Ty(M.getContext());
6839 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6840 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6841 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6842 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6843 CLI->setLastIter(PLastIter);
6844
6845 // At the end of the preheader, prepare for calling the "init" function by
6846 // storing the current loop bounds into the allocated space. A canonical loop
6847 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6848 // and produces an inclusive upper bound.
6849 BasicBlock *PreHeader = CLI->getPreheader();
6850 Builder.SetInsertPoint(PreHeader->getTerminator());
6851 Constant *One = ConstantInt::get(IVTy, 1);
6852 Builder.CreateStore(One, PLowerBound);
6853 Value *UpperBound = CLI->getTripCount();
6854 Builder.CreateStore(UpperBound, PUpperBound);
6855 Builder.CreateStore(One, PStride);
6856
6857 BasicBlock *Header = CLI->getHeader();
6858 BasicBlock *Exit = CLI->getExit();
6859 BasicBlock *Cond = CLI->getCond();
6860 BasicBlock *Latch = CLI->getLatch();
6861 InsertPointTy AfterIP = CLI->getAfterIP();
6862
6863 // The CLI will be "broken" in the code below, as the loop is no longer
6864 // a valid canonical loop.
6865
6866 if (!Chunk)
6867 Chunk = One;
6868
6869 Value *ThreadNum =
6870 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6871
6872 Constant *SchedulingType =
6873 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6874
6875 // Call the "init" function.
6876 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6877 /* LowerBound */ One, UpperBound,
6878 /* step */ One, Chunk});
6879
6880 // An outer loop around the existing one.
6881 BasicBlock *OuterCond = BasicBlock::Create(
6882 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6883 PreHeader->getParent());
6884 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6885 Builder.SetInsertPoint(OuterCond, OuterCond->getFirstInsertionPt());
6887 DynamicNext,
6888 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6889 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6890 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6891 Value *LowerBound =
6892 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6893 Builder.CreateCondBr(MoreWork, Header, Exit);
6894
6895 // Change PHI-node in loop header to use outer cond rather than preheader,
6896 // and set IV to the LowerBound.
6897 Instruction *Phi = &Header->front();
6898 auto *PI = cast<PHINode>(Phi);
6899 PI->setIncomingBlock(0, OuterCond);
6900 PI->setIncomingValue(0, LowerBound);
6901
6902 // Then set the pre-header to jump to the OuterCond
6903 Instruction *Term = PreHeader->getTerminator();
6904 auto *Br = cast<UncondBrInst>(Term);
6905 Br->setSuccessor(OuterCond);
6906
6907 // Modify the inner condition:
6908 // * Use the UpperBound returned from the DynamicNext call.
6909 // * jump to the loop outer loop when done with one of the inner loops.
6910 Builder.SetInsertPoint(Cond, Cond->getFirstInsertionPt());
6911 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
6912 Instruction *Comp = &*Builder.GetInsertPoint();
6913 auto *CI = cast<CmpInst>(Comp);
6914 CI->setOperand(1, UpperBound);
6915 // Redirect the inner exit to branch to outer condition.
6916 Instruction *Branch = &Cond->back();
6917 auto *BI = cast<CondBrInst>(Branch);
6918 assert(BI->getSuccessor(1) == Exit);
6919 BI->setSuccessor(1, OuterCond);
6920
6921 // Call the "fini" function if "ordered" is present in wsloop directive.
6922 if (Ordered) {
6923 Builder.SetInsertPoint(&Latch->back());
6924 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
6925 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
6926 }
6927
6928 // Add the barrier if requested.
6929 if (NeedsBarrier) {
6930 Builder.SetInsertPoint(&Exit->back());
6931 InsertPointOrErrorTy BarrierIP =
6933 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6934 /* CheckCancelFlag */ false);
6935 if (!BarrierIP)
6936 return BarrierIP.takeError();
6937 }
6938
6939 CLI->invalidate();
6940 return AfterIP;
6941}
6942
6943/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
6944/// after this \p OldTarget will be orphaned.
6946 BasicBlock *NewTarget, DebugLoc DL) {
6947 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
6948 redirectTo(Pred, NewTarget, DL);
6949}
6950
6952 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
6953 // We add a block to BBsToKeep iff we have proven it has an external use.
6955
6956 while (true) {
6957 bool Changed = false;
6958
6959 for (BasicBlock *BB : BBs) {
6960 if (BBsToKeep.contains(BB))
6961 continue;
6962
6963 for (Use &U : BB->uses()) {
6964 auto *UseInst = dyn_cast<Instruction>(U.getUser());
6965 if (!UseInst)
6966 continue;
6967 BasicBlock *UseBB = UseInst->getParent();
6968 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
6969 BBsToKeep.insert(BB);
6970 Changed = true;
6971 break;
6972 }
6973 }
6974 }
6975
6976 if (!Changed)
6977 break;
6978 }
6979
6981 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
6982 DeleteDeadBlocks(BBsToDelete);
6983}
6984
6985CanonicalLoopInfo *
6987 InsertPointTy ComputeIP) {
6988 assert(Loops.size() >= 1 && "At least one loop required");
6989 size_t NumLoops = Loops.size();
6990
6991 // Nothing to do if there is already just one loop.
6992 if (NumLoops == 1)
6993 return Loops.front();
6994
6995 CanonicalLoopInfo *Outermost = Loops.front();
6996 CanonicalLoopInfo *Innermost = Loops.back();
6997 BasicBlock *OrigPreheader = Outermost->getPreheader();
6998 BasicBlock *OrigAfter = Outermost->getAfter();
6999 Function *F = OrigPreheader->getParent();
7000
7001 // Loop control blocks that may become orphaned later.
7002 SmallVector<BasicBlock *, 12> OldControlBBs;
7003 OldControlBBs.reserve(6 * Loops.size());
7005 Loop->collectControlBlocks(OldControlBBs);
7006
7007 // Setup the IRBuilder for inserting the trip count computation.
7008 Builder.SetCurrentDebugLocation(DL);
7009 if (ComputeIP.isSet())
7010 Builder.restoreIP(ComputeIP);
7011 else
7012 Builder.restoreIP(Outermost->getPreheaderIP());
7013
7014 // Derive the collapsed' loop trip count.
7015 // TODO: Find common/largest indvar type.
7016 Value *CollapsedTripCount = nullptr;
7017 for (CanonicalLoopInfo *L : Loops) {
7018 assert(L->isValid() &&
7019 "All loops to collapse must be valid canonical loops");
7020 Value *OrigTripCount = L->getTripCount();
7021 if (!CollapsedTripCount) {
7022 CollapsedTripCount = OrigTripCount;
7023 continue;
7024 }
7025
7026 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7027 CollapsedTripCount =
7028 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7029 }
7030
7031 // Create the collapsed loop control flow.
7032 CanonicalLoopInfo *Result =
7033 createLoopSkeleton(DL, CollapsedTripCount, F,
7034 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7035 /*IsCollapsed=*/true);
7036
7037 // Build the collapsed loop body code.
7038 // Start with deriving the input loop induction variables from the collapsed
7039 // one, using a divmod scheme. To preserve the original loops' order, the
7040 // innermost loop use the least significant bits.
7041 Builder.restoreIP(Result->getBodyIP());
7042
7043 Value *Leftover = Result->getIndVar();
7044 SmallVector<Value *> NewIndVars;
7045 NewIndVars.resize(NumLoops);
7046 for (int i = NumLoops - 1; i >= 1; --i) {
7047 Value *OrigTripCount = Loops[i]->getTripCount();
7048
7049 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7050 NewIndVars[i] = NewIndVar;
7051
7052 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7053 }
7054 // Outermost loop gets all the remaining bits.
7055 NewIndVars[0] = Leftover;
7056
7057 // Construct the loop body control flow.
7058 // We progressively construct the branch structure following in direction of
7059 // the control flow, from the leading in-between code, the loop nest body, the
7060 // trailing in-between code, and rejoining the collapsed loop's latch.
7061 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7062 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7063 // its predecessors as sources.
7064 BasicBlock *ContinueBlock = Result->getBody();
7065 BasicBlock *ContinuePred = nullptr;
7066 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7067 BasicBlock *NextSrc) {
7068 if (ContinueBlock)
7069 redirectTo(ContinueBlock, Dest, DL);
7070 else
7071 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7072
7073 ContinueBlock = nullptr;
7074 ContinuePred = NextSrc;
7075 };
7076
7077 // The code before the nested loop of each level.
7078 // Because we are sinking it into the nest, it will be executed more often
7079 // that the original loop. More sophisticated schemes could keep track of what
7080 // the in-between code is and instantiate it only once per thread.
7081 for (size_t i = 0; i < NumLoops - 1; ++i)
7082 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7083
7084 // Connect the loop nest body.
7085 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7086
7087 // The code after the nested loop at each level.
7088 for (size_t i = NumLoops - 1; i > 0; --i)
7089 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7090
7091 // Connect the finished loop to the collapsed loop latch.
7092 ContinueWith(Result->getLatch(), nullptr);
7093
7094 // Replace the input loops with the new collapsed loop.
7095 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7096 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7097
7098 // Replace the input loop indvars with the derived ones.
7099 for (size_t i = 0; i < NumLoops; ++i)
7100 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7101
7102 // Remove unused parts of the input loops.
7103 removeUnusedBlocksFromParent(OldControlBBs);
7104
7105 for (CanonicalLoopInfo *L : Loops)
7106 L->invalidate();
7107
7108#ifndef NDEBUG
7109 Result->assertOK();
7110#endif
7111 return Result;
7112}
7113
7114std::vector<CanonicalLoopInfo *>
7116 ArrayRef<Value *> TileSizes) {
7117 assert(TileSizes.size() == Loops.size() &&
7118 "Must pass as many tile sizes as there are loops");
7119 int NumLoops = Loops.size();
7120 assert(NumLoops >= 1 && "At least one loop to tile required");
7121
7122 CanonicalLoopInfo *OutermostLoop = Loops.front();
7123 CanonicalLoopInfo *InnermostLoop = Loops.back();
7124 Function *F = OutermostLoop->getBody()->getParent();
7125 BasicBlock *InnerEnter = InnermostLoop->getBody();
7126 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7127
7128 // Loop control blocks that may become orphaned later.
7129 SmallVector<BasicBlock *, 12> OldControlBBs;
7130 OldControlBBs.reserve(6 * Loops.size());
7132 Loop->collectControlBlocks(OldControlBBs);
7133
7134 // Collect original trip counts and induction variable to be accessible by
7135 // index. Also, the structure of the original loops is not preserved during
7136 // the construction of the tiled loops, so do it before we scavenge the BBs of
7137 // any original CanonicalLoopInfo.
7138 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7139 for (CanonicalLoopInfo *L : Loops) {
7140 assert(L->isValid() && "All input loops must be valid canonical loops");
7141 OrigTripCounts.push_back(L->getTripCount());
7142 OrigIndVars.push_back(L->getIndVar());
7143 }
7144
7145 // Collect the code between loop headers. These may contain SSA definitions
7146 // that are used in the loop nest body. To be usable with in the innermost
7147 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7148 // these instructions may be executed more often than before the tiling.
7149 // TODO: It would be sufficient to only sink them into body of the
7150 // corresponding tile loop.
7152 for (int i = 0; i < NumLoops - 1; ++i) {
7153 CanonicalLoopInfo *Surrounding = Loops[i];
7154 CanonicalLoopInfo *Nested = Loops[i + 1];
7155
7156 BasicBlock *EnterBB = Surrounding->getBody();
7157 BasicBlock *ExitBB = Nested->getHeader();
7158 InbetweenCode.emplace_back(EnterBB, ExitBB);
7159 }
7160
7161 // Compute the trip counts of the floor loops.
7162 Builder.SetCurrentDebugLocation(DL);
7163 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7164 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7165 for (int i = 0; i < NumLoops; ++i) {
7166 Value *TileSize = TileSizes[i];
7167 Value *OrigTripCount = OrigTripCounts[i];
7168 Type *IVType = OrigTripCount->getType();
7169
7170 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7171 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7172
7173 // 0 if tripcount divides the tilesize, 1 otherwise.
7174 // 1 means we need an additional iteration for a partial tile.
7175 //
7176 // Unfortunately we cannot just use the roundup-formula
7177 // (tripcount + tilesize - 1)/tilesize
7178 // because the summation might overflow. We do not want introduce undefined
7179 // behavior when the untiled loop nest did not.
7180 Value *FloorTripOverflow =
7181 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7182
7183 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7184 Value *FloorTripCount =
7185 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7186 "omp_floor" + Twine(i) + ".tripcount", true);
7187
7188 // Remember some values for later use.
7189 FloorCompleteCount.push_back(FloorCompleteTripCount);
7190 FloorCount.push_back(FloorTripCount);
7191 FloorRems.push_back(FloorTripRem);
7192 }
7193
7194 // Generate the new loop nest, from the outermost to the innermost.
7195 std::vector<CanonicalLoopInfo *> Result;
7196 Result.reserve(NumLoops * 2);
7197
7198 // The basic block of the surrounding loop that enters the nest generated
7199 // loop.
7200 BasicBlock *Enter = OutermostLoop->getPreheader();
7201
7202 // The basic block of the surrounding loop where the inner code should
7203 // continue.
7204 BasicBlock *Continue = OutermostLoop->getAfter();
7205
7206 // Where the next loop basic block should be inserted.
7207 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7208
7209 auto EmbeddNewLoop =
7210 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7211 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7212 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7213 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7214 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7215 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7216
7217 // Setup the position where the next embedded loop connects to this loop.
7218 Enter = EmbeddedLoop->getBody();
7219 Continue = EmbeddedLoop->getLatch();
7220 OutroInsertBefore = EmbeddedLoop->getLatch();
7221 return EmbeddedLoop;
7222 };
7223
7224 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7225 const Twine &NameBase) {
7226 for (auto P : enumerate(TripCounts)) {
7227 CanonicalLoopInfo *EmbeddedLoop =
7228 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7229 Result.push_back(EmbeddedLoop);
7230 }
7231 };
7232
7233 EmbeddNewLoops(FloorCount, "floor");
7234
7235 // Within the innermost floor loop, emit the code that computes the tile
7236 // sizes.
7237 Builder.SetInsertPoint(Enter->getTerminator());
7238 SmallVector<Value *, 4> TileCounts;
7239 for (int i = 0; i < NumLoops; ++i) {
7240 CanonicalLoopInfo *FloorLoop = Result[i];
7241 Value *TileSize = TileSizes[i];
7242
7243 Value *FloorIsEpilogue =
7244 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7245 Value *TileTripCount =
7246 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7247
7248 TileCounts.push_back(TileTripCount);
7249 }
7250
7251 // Create the tile loops.
7252 EmbeddNewLoops(TileCounts, "tile");
7253
7254 // Insert the inbetween code into the body.
7255 BasicBlock *BodyEnter = Enter;
7256 BasicBlock *BodyEntered = nullptr;
7257 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7258 BasicBlock *EnterBB = P.first;
7259 BasicBlock *ExitBB = P.second;
7260
7261 if (BodyEnter)
7262 redirectTo(BodyEnter, EnterBB, DL);
7263 else
7264 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7265
7266 BodyEnter = nullptr;
7267 BodyEntered = ExitBB;
7268 }
7269
7270 // Append the original loop nest body into the generated loop nest body.
7271 if (BodyEnter)
7272 redirectTo(BodyEnter, InnerEnter, DL);
7273 else
7274 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7276
7277 // Replace the original induction variable with an induction variable computed
7278 // from the tile and floor induction variables.
7279 Builder.restoreIP(Result.back()->getBodyIP());
7280 for (int i = 0; i < NumLoops; ++i) {
7281 CanonicalLoopInfo *FloorLoop = Result[i];
7282 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7283 Value *OrigIndVar = OrigIndVars[i];
7284 Value *Size = TileSizes[i];
7285
7286 Value *Scale =
7287 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7288 Value *Shift =
7289 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7290 OrigIndVar->replaceAllUsesWith(Shift);
7291 }
7292
7293 // Remove unused parts of the original loops.
7294 removeUnusedBlocksFromParent(OldControlBBs);
7295
7296 for (CanonicalLoopInfo *L : Loops)
7297 L->invalidate();
7298
7299#ifndef NDEBUG
7300 for (CanonicalLoopInfo *GenL : Result)
7301 GenL->assertOK();
7302#endif
7303 return Result;
7304}
7305
7306/// Attach metadata \p Properties to the basic block described by \p BB. If the
7307/// basic block already has metadata, the basic block properties are appended.
7309 ArrayRef<Metadata *> Properties) {
7310 // Nothing to do if no property to attach.
7311 if (Properties.empty())
7312 return;
7313
7314 LLVMContext &Ctx = BB->getContext();
7315 SmallVector<Metadata *> NewProperties;
7316 NewProperties.push_back(nullptr);
7317
7318 // If the basic block already has metadata, prepend it to the new metadata.
7319 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7320 if (Existing)
7321 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7322
7323 append_range(NewProperties, Properties);
7324 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7325 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7326
7327 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7328}
7329
7330/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7331/// loop already has metadata, the loop properties are appended.
7333 ArrayRef<Metadata *> Properties) {
7334 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7335
7336 // Attach metadata to the loop's latch
7337 BasicBlock *Latch = Loop->getLatch();
7338 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7339 addBasicBlockMetadata(Latch, Properties);
7340}
7341
7342/// Attach llvm.access.group metadata to the memref instructions of \p Block
7344 LoopInfo &LI) {
7345 for (Instruction &I : *Block) {
7346 if (I.mayReadOrWriteMemory()) {
7347 // TODO: This instruction may already have access group from
7348 // other pragmas e.g. #pragma clang loop vectorize. Append
7349 // so that the existing metadata is not overwritten.
7350 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7351 }
7352 }
7353}
7354
7355CanonicalLoopInfo *
7357 CanonicalLoopInfo *firstLoop = Loops.front();
7358 CanonicalLoopInfo *lastLoop = Loops.back();
7359 Function *F = firstLoop->getPreheader()->getParent();
7360
7361 // Loop control blocks that will become orphaned later
7362 SmallVector<BasicBlock *> oldControlBBs;
7364 Loop->collectControlBlocks(oldControlBBs);
7365
7366 // Collect original trip counts
7367 SmallVector<Value *> origTripCounts;
7368 for (CanonicalLoopInfo *L : Loops) {
7369 assert(L->isValid() && "All input loops must be valid canonical loops");
7370 origTripCounts.push_back(L->getTripCount());
7371 }
7372
7373 Builder.SetCurrentDebugLocation(DL);
7374
7375 // Compute max trip count.
7376 // The fused loop will be from 0 to max(origTripCounts)
7377 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7378 F, firstLoop->getHeader());
7379 Builder.SetInsertPoint(TCBlock);
7380 Value *fusedTripCount = nullptr;
7381 for (CanonicalLoopInfo *L : Loops) {
7382 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7383 Value *origTripCount = L->getTripCount();
7384 if (!fusedTripCount) {
7385 fusedTripCount = origTripCount;
7386 continue;
7387 }
7388 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7389 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7390 ".omp.fuse.tc");
7391 }
7392
7393 // Generate new loop
7394 CanonicalLoopInfo *fused =
7395 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7396 lastLoop->getLatch(), "fused");
7397
7398 // Replace original loops with the fused loop
7399 // Preheader and After are not considered inside the CLI.
7400 // These are used to compute the individual TCs of the loops
7401 // so they have to be put before the resulting fused loop.
7402 // Moving them up for readability.
7403 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7404 Loops[i]->getPreheader()->moveBefore(TCBlock);
7405 Loops[i]->getAfter()->moveBefore(TCBlock);
7406 }
7407 lastLoop->getPreheader()->moveBefore(TCBlock);
7408
7409 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7410 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7411 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7412 }
7413 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7414 redirectTo(TCBlock, fused->getPreheader(), DL);
7415 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7416
7417 // Build the fused body
7418 // Create new Blocks with conditions that jump to the original loop bodies
7420 SmallVector<Value *> condValues;
7421 for (size_t i = 0; i < Loops.size(); ++i) {
7422 BasicBlock *condBlock = BasicBlock::Create(
7423 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7424 Builder.SetInsertPoint(condBlock);
7425 Value *condValue =
7426 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7427 condBBs.push_back(condBlock);
7428 condValues.push_back(condValue);
7429 }
7430 // Join the condition blocks with the bodies of the original loops
7431 redirectTo(fused->getBody(), condBBs[0], DL);
7432 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7433 Builder.SetInsertPoint(condBBs[i]);
7434 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7435 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7436 // Replace the IV with the fused IV
7437 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7438 }
7439 // Last body jumps to the created end body block
7440 Builder.SetInsertPoint(condBBs.back());
7441 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7442 fused->getLatch());
7443 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7444 // Replace the IV with the fused IV
7445 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7446
7447 // The loop latch must have only one predecessor. Currently it is branched to
7448 // from both the last condition block and the last loop body
7449 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7450 "omp.fused.pre_latch");
7451
7452 // Remove unused parts
7453 removeUnusedBlocksFromParent(oldControlBBs);
7454
7455 // Invalidate old CLIs
7456 for (CanonicalLoopInfo *L : Loops)
7457 L->invalidate();
7458
7459#ifndef NDEBUG
7460 fused->assertOK();
7461#endif
7462 return fused;
7463}
7464
7466 LLVMContext &Ctx = Builder.getContext();
7468 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7469 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7470}
7471
7473 LLVMContext &Ctx = Builder.getContext();
7475 Loop, {
7476 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7477 });
7478}
7479
7480void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7481 Value *IfCond, ValueToValueMapTy &VMap,
7482 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7483 const Twine &NamePrefix) {
7484 Function *F = CanonicalLoop->getFunction();
7485
7486 // We can't do
7487 // if (cond) {
7488 // simd_loop;
7489 // } else {
7490 // non_simd_loop;
7491 // }
7492 // because then the CanonicalLoopInfo would only point to one of the loops:
7493 // leading to other constructs operating on the same loop to malfunction.
7494 // Instead generate
7495 // while (...) {
7496 // if (cond) {
7497 // simd_body;
7498 // } else {
7499 // not_simd_body;
7500 // }
7501 // }
7502 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7503 // body at -O3
7504
7505 // Define where if branch should be inserted
7506 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7507
7508 // Create additional blocks for the if statement
7509 BasicBlock *Cond = SplitBeforeIt->getParent();
7510 llvm::LLVMContext &C = Cond->getContext();
7512 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7514 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7515
7516 // Create if condition branch.
7517 Builder.SetInsertPoint(SplitBeforeIt);
7518 Instruction *BrInstr =
7519 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7520 InsertPointTy IP{BrInstr->getParent(), ++BrInstr->getIterator()};
7521 // Then block contains branch to omp loop body which needs to be vectorized
7522 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7523 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7524
7525 Builder.SetInsertPoint(ElseBlock);
7526
7527 // Clone loop for the else branch
7529
7530 SmallVector<BasicBlock *, 8> ExistingBlocks;
7531 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7532 ExistingBlocks.push_back(ThenBlock);
7533 ExistingBlocks.append(L->block_begin(), L->block_end());
7534 // Cond is the block that has the if clause condition
7535 // LoopCond is omp_loop.cond
7536 // LoopHeader is omp_loop.header
7537 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7538 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7539 assert(LoopCond && LoopHeader && "Invalid loop structure");
7540 for (BasicBlock *Block : ExistingBlocks) {
7541 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7542 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7543 continue;
7544 }
7545 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7546
7547 // fix name not to be omp.if.then
7548 if (Block == ThenBlock)
7549 NewBB->setName(NamePrefix + ".if.else");
7550
7551 NewBB->moveBefore(CanonicalLoop->getExit());
7552 VMap[Block] = NewBB;
7553 NewBlocks.push_back(NewBB);
7554 }
7555 remapInstructionsInBlocks(NewBlocks, VMap);
7556 Builder.CreateBr(NewBlocks.front());
7557
7558 // The loop latch must have only one predecessor. Currently it is branched to
7559 // from both the 'then' and 'else' branches.
7560 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7561 NamePrefix + ".pre_latch");
7562
7563 // Ensure that the then block is added to the loop so we add the attributes in
7564 // the next step
7565 L->addBasicBlockToLoop(ThenBlock, LI);
7566}
7567
7568unsigned
7570 const StringMap<bool> &Features) {
7571 if (TargetTriple.isX86()) {
7572 if (Features.lookup("avx512f"))
7573 return 512;
7574 else if (Features.lookup("avx"))
7575 return 256;
7576 return 128;
7577 }
7578 if (TargetTriple.isPPC())
7579 return 128;
7580 if (TargetTriple.isWasm())
7581 return 128;
7582 return 0;
7583}
7584
7586 MapVector<Value *, Value *> AlignedVars,
7587 Value *IfCond, OrderKind Order,
7588 ConstantInt *Simdlen, ConstantInt *Safelen) {
7589 LLVMContext &Ctx = Builder.getContext();
7590
7591 Function *F = CanonicalLoop->getFunction();
7592
7593 // Blocks must have terminators.
7594 // FIXME: Don't run analyses on incomplete/invalid IR.
7596 for (BasicBlock &BB : *F)
7597 if (!BB.hasTerminator())
7598 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7599
7600 // TODO: We should not rely on pass manager. Currently we use pass manager
7601 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7602 // object. We should have a method which returns all blocks between
7603 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7605 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7606 FAM.registerPass([]() { return LoopAnalysis(); });
7607 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7608
7609 LoopAnalysis LIA;
7610 LoopInfo &&LI = LIA.run(*F, FAM);
7611
7612 for (Instruction *I : UIs)
7613 I->eraseFromParent();
7614
7615 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7616 if (AlignedVars.size()) {
7617 InsertPointTy IP = Builder.saveIP();
7618 for (auto &AlignedItem : AlignedVars) {
7619 Value *AlignedPtr = AlignedItem.first;
7620 Value *Alignment = AlignedItem.second;
7621 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7622 Builder.SetInsertPoint(loadInst->getNextNode());
7623 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7624 Alignment);
7625 }
7626 Builder.restoreIP(IP);
7627 }
7628
7629 if (IfCond) {
7630 ValueToValueMapTy VMap;
7631 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7632 }
7633
7635
7636 // Get the basic blocks from the loop in which memref instructions
7637 // can be found.
7638 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7639 // preferably without running any passes.
7640 for (BasicBlock *Block : L->getBlocks()) {
7641 if (Block == CanonicalLoop->getCond() ||
7642 Block == CanonicalLoop->getHeader())
7643 continue;
7644 Reachable.insert(Block);
7645 }
7646
7647 SmallVector<Metadata *> LoopMDList;
7648
7649 // In presence of finite 'safelen', it may be unsafe to mark all
7650 // the memory instructions parallel, because loop-carried
7651 // dependences of 'safelen' iterations are possible.
7652 // If clause order(concurrent) is specified then the memory instructions
7653 // are marked parallel even if 'safelen' is finite.
7654 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7655 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7656
7657 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7658 // versions so we can't add the loop attributes in that case.
7659 if (IfCond) {
7660 // we can still add llvm.loop.parallel_access
7661 addLoopMetadata(CanonicalLoop, LoopMDList);
7662 return;
7663 }
7664
7665 // Use the above access group metadata to create loop level
7666 // metadata, which should be distinct for each loop.
7667 LoopMDList.push_back(
7668 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7669
7670 if (Simdlen || Safelen) {
7671 // If both simdlen and safelen clauses are specified, the value of the
7672 // simdlen parameter must be less than or equal to the value of the safelen
7673 // parameter. Therefore, use safelen only in the absence of simdlen.
7674 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7675 LoopMDList.push_back(
7676 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7677 ConstantAsMetadata::get(VectorizeWidth)}));
7678 }
7679
7680 addLoopMetadata(CanonicalLoop, LoopMDList);
7681}
7682
7683/// Create the TargetMachine object to query the backend for optimization
7684/// preferences.
7685///
7686/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7687/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7688/// needed for the LLVM pass pipline. We use some default options to avoid
7689/// having to pass too many settings from the frontend that probably do not
7690/// matter.
7691///
7692/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7693/// method. If we are going to use TargetMachine for more purposes, especially
7694/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7695/// might become be worth requiring front-ends to pass on their TargetMachine,
7696/// or at least cache it between methods. Note that while fontends such as Clang
7697/// have just a single main TargetMachine per translation unit, "target-cpu" and
7698/// "target-features" that determine the TargetMachine are per-function and can
7699/// be overrided using __attribute__((target("OPTIONS"))).
7700static std::unique_ptr<TargetMachine>
7702 Module *M = F->getParent();
7703
7704 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7705 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7706 const llvm::Triple &Triple = M->getTargetTriple();
7707
7708 std::string Error;
7710 if (!TheTarget)
7711 return {};
7712
7714 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7715 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7716 /*CodeModel=*/std::nullopt, OptLevel));
7717}
7718
7719/// Heuristically determine the best-performant unroll factor for \p CLI. This
7720/// depends on the target processor. We are re-using the same heuristics as the
7721/// LoopUnrollPass.
7723 Function *F = CLI->getFunction();
7724
7725 // Assume the user requests the most aggressive unrolling, even if the rest of
7726 // the code is optimized using a lower setting.
7728 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7729
7730 // Blocks must have terminators.
7731 // FIXME: Don't run analyses on incomplete/invalid IR.
7733 for (BasicBlock &BB : *F)
7734 if (!BB.hasTerminator())
7735 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7736
7738 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7739 FAM.registerPass([]() { return AssumptionAnalysis(); });
7740 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7741 FAM.registerPass([]() { return LoopAnalysis(); });
7742 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7743 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7744 TargetIRAnalysis TIRA;
7745 if (TM)
7746 TIRA = TargetIRAnalysis(
7747 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7748 FAM.registerPass([&]() { return TIRA; });
7749
7750 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7752 ScalarEvolution &&SE = SEA.run(*F, FAM);
7754 DominatorTree &&DT = DTA.run(*F, FAM);
7755 LoopAnalysis LIA;
7756 LoopInfo &&LI = LIA.run(*F, FAM);
7758 AssumptionCache &&AC = ACT.run(*F, FAM);
7760
7761 for (Instruction *I : UIs)
7762 I->eraseFromParent();
7763
7764 Loop *L = LI.getLoopFor(CLI->getHeader());
7765 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7766
7768 L, SE, TTI,
7769 /*BlockFrequencyInfo=*/nullptr,
7770 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7771 /*UserThreshold=*/std::nullopt,
7772 /*UserAllowPartial=*/true,
7773 /*UserAllowRuntime=*/true,
7774 /*UserUpperBound=*/std::nullopt,
7775 /*UserFullUnrollMaxCount=*/std::nullopt);
7776
7777 UP.Force = true;
7778
7779 // Account for additional optimizations taking place before the LoopUnrollPass
7780 // would unroll the loop.
7783
7784 // Use normal unroll factors even if the rest of the code is optimized for
7785 // size.
7788
7789 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7790 << " Threshold=" << UP.Threshold << "\n"
7791 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7792 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7793 << " PartialOptSizeThreshold="
7794 << UP.PartialOptSizeThreshold << "\n");
7795
7796 // Disable peeling.
7799 /*UserAllowPeeling=*/false,
7800 /*UserAllowProfileBasedPeeling=*/false,
7801 /*UnrollingSpecficValues=*/false);
7802
7804 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7805
7806 // Assume that reads and writes to stack variables can be eliminated by
7807 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7808 // size.
7809 for (BasicBlock *BB : L->blocks()) {
7810 for (Instruction &I : *BB) {
7811 Value *Ptr;
7812 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7813 Ptr = Load->getPointerOperand();
7814 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7815 Ptr = Store->getPointerOperand();
7816 } else
7817 continue;
7818
7819 Ptr = Ptr->stripPointerCasts();
7820
7821 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7822 if (Alloca->getParent() == &F->getEntryBlock())
7823 EphValues.insert(&I);
7824 }
7825 }
7826 }
7827
7828 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7829
7830 // Loop is not unrollable if the loop contains certain instructions.
7831 if (!UCE.canUnroll()) {
7832 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7833 return 1;
7834 }
7835
7836 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7837 << "\n");
7838
7839 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7840 // be able to use it.
7841 int TripCount = 0;
7842 int MaxTripCount = 0;
7843 bool MaxOrZero = false;
7844 unsigned TripMultiple = 0;
7845
7846 unsigned Factor =
7847 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7848 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7849 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7850
7851 // This function returns 1 to signal to not unroll a loop.
7852 if (Factor == 0)
7853 return 1;
7854 return Factor;
7855}
7856
7858 int32_t Factor,
7859 CanonicalLoopInfo **UnrolledCLI) {
7860 assert(Factor >= 0 && "Unroll factor must not be negative");
7861
7862 Function *F = Loop->getFunction();
7863 LLVMContext &Ctx = F->getContext();
7864
7865 // If the unrolled loop is not used for another loop-associated directive, it
7866 // is sufficient to add metadata for the LoopUnrollPass.
7867 if (!UnrolledCLI) {
7868 SmallVector<Metadata *, 2> LoopMetadata;
7869 LoopMetadata.push_back(
7870 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7871
7872 if (Factor >= 1) {
7874 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7875 LoopMetadata.push_back(MDNode::get(
7876 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7877 }
7878
7879 addLoopMetadata(Loop, LoopMetadata);
7880 return;
7881 }
7882
7883 // Heuristically determine the unroll factor.
7884 if (Factor == 0)
7886
7887 // No change required with unroll factor 1.
7888 if (Factor == 1) {
7889 *UnrolledCLI = Loop;
7890 return;
7891 }
7892
7893 assert(Factor >= 2 &&
7894 "unrolling only makes sense with a factor of 2 or larger");
7895
7896 Type *IndVarTy = Loop->getIndVarType();
7897
7898 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7899 // unroll the inner loop.
7900 Value *FactorVal =
7901 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7902 /*isSigned=*/false));
7903 std::vector<CanonicalLoopInfo *> LoopNest =
7904 tileLoops(DL, {Loop}, {FactorVal});
7905 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7906 *UnrolledCLI = LoopNest[0];
7907 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7908
7909 // LoopUnrollPass can only fully unroll loops with constant trip count.
7910 // Unroll by the unroll factor with a fallback epilog for the remainder
7911 // iterations if necessary.
7913 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7915 InnerLoop,
7916 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7918 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
7919
7920#ifndef NDEBUG
7921 (*UnrolledCLI)->assertOK();
7922#endif
7923}
7924
7927 llvm::Value *BufSize, llvm::Value *CpyBuf,
7928 llvm::Value *CpyFn, llvm::Value *DidIt) {
7929 if (!updateToLocation(Loc))
7930 return Loc.IP;
7931
7932 uint32_t SrcLocStrSize;
7933 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7934 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7935 Value *ThreadId = getOrCreateThreadID(Ident);
7936
7937 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
7938
7939 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
7940
7941 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
7942 createRuntimeFunctionCall(Fn, Args);
7943
7944 return Builder.saveIP();
7945}
7946
7948 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7949 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
7951
7952 if (!updateToLocation(Loc))
7953 return Loc.IP;
7954
7955 // If needed allocate and initialize `DidIt` with 0.
7956 // DidIt: flag variable: 1=single thread; 0=not single thread.
7957 llvm::Value *DidIt = nullptr;
7958 if (!CPVars.empty()) {
7959 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
7960 Builder.CreateStore(Builder.getInt32(0), DidIt);
7961 }
7962
7963 Directive OMPD = Directive::OMPD_single;
7964 uint32_t SrcLocStrSize;
7965 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7966 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7967 Value *ThreadId = getOrCreateThreadID(Ident);
7968 Value *Args[] = {Ident, ThreadId};
7969
7970 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
7971 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
7972
7973 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
7974 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
7975
7976 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
7977 if (Error Err = FiniCB(IP))
7978 return Err;
7979
7980 // The thread that executes the single region must set `DidIt` to 1.
7981 // This is used by __kmpc_copyprivate, to know if the caller is the
7982 // single thread or not.
7983 if (DidIt)
7984 Builder.CreateStore(Builder.getInt32(1), DidIt);
7985
7986 return Error::success();
7987 };
7988
7989 // generates the following:
7990 // if (__kmpc_single()) {
7991 // .... single region ...
7992 // __kmpc_end_single
7993 // }
7994 // __kmpc_copyprivate
7995 // __kmpc_barrier
7996
7997 InsertPointOrErrorTy AfterIP =
7998 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
7999 /*Conditional*/ true,
8000 /*hasFinalize*/ true);
8001 if (!AfterIP)
8002 return AfterIP.takeError();
8003
8004 if (DidIt) {
8005 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8006 // NOTE BufSize is currently unused, so just pass 0.
8008 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8009 CPFuncs[I], DidIt);
8010 // NOTE __kmpc_copyprivate already inserts a barrier
8011 } else if (!IsNowait) {
8012 InsertPointOrErrorTy AfterIP =
8014 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8015 /* CheckCancelFlag */ false);
8016 if (!AfterIP)
8017 return AfterIP.takeError();
8018 }
8019 return Builder.saveIP();
8020}
8021
8024 BodyGenCallbackTy BodyGenCB,
8025 FinalizeCallbackTy FiniCB, bool IsNowait) {
8026
8027 if (!updateToLocation(Loc))
8028 return Loc.IP;
8029
8030 // All threads execute the scope body — no conditional entry.
8031 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8032 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8033 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8034 /*IsCancellable=*/false);
8035 if (!AfterIP)
8036 return AfterIP.takeError();
8037
8038 Builder.restoreIP(*AfterIP);
8039 if (!IsNowait) {
8040 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8041 omp::Directive::OMPD_unknown,
8042 /*ForceSimpleCall=*/false,
8043 /*CheckCancelFlag=*/false);
8044 if (!AfterIP)
8045 return AfterIP.takeError();
8046 }
8047 return Builder.saveIP();
8048}
8049
8051 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8052 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8053
8054 if (!updateToLocation(Loc))
8055 return Loc.IP;
8056
8057 Directive OMPD = Directive::OMPD_critical;
8058 uint32_t SrcLocStrSize;
8059 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8060 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8061 Value *ThreadId = getOrCreateThreadID(Ident);
8062 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8063 Value *Args[] = {Ident, ThreadId, LockVar};
8064
8065 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8066 Function *RTFn = nullptr;
8067 if (HintInst) {
8068 // Add Hint to entry Args and create call
8069 EnterArgs.push_back(HintInst);
8070 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8071 } else {
8072 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8073 }
8074 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8075
8076 Function *ExitRTLFn =
8077 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8078 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8079
8080 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8081 /*Conditional*/ false, /*hasFinalize*/ true);
8082}
8083
8086 InsertPointTy AllocaIP, unsigned NumLoops,
8087 ArrayRef<llvm::Value *> StoreValues,
8088 const Twine &Name, bool IsDependSource) {
8089 assert(
8090 llvm::all_of(StoreValues,
8091 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8092 "OpenMP runtime requires depend vec with i64 type");
8093
8094 if (!updateToLocation(Loc))
8095 return Loc.IP;
8096
8097 // Allocate space for vector and generate alloc instruction.
8098 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8099 Builder.restoreIP(AllocaIP);
8100 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8101 ArgsBase->setAlignment(Align(8));
8103
8104 // Store the index value with offset in depend vector.
8105 for (unsigned I = 0; I < NumLoops; ++I) {
8106 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8107 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8108 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8109 STInst->setAlignment(Align(8));
8110 }
8111
8112 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8113 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8114
8115 uint32_t SrcLocStrSize;
8116 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8117 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8118 Value *ThreadId = getOrCreateThreadID(Ident);
8119 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8120
8121 Function *RTLFn = nullptr;
8122 if (IsDependSource)
8123 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8124 else
8125 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8126 createRuntimeFunctionCall(RTLFn, Args);
8127
8128 return Builder.saveIP();
8129}
8130
8132 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8133 FinalizeCallbackTy FiniCB, bool IsThreads) {
8134 if (!updateToLocation(Loc))
8135 return Loc.IP;
8136
8137 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8138 Instruction *EntryCall = nullptr;
8139 Instruction *ExitCall = nullptr;
8140
8141 if (IsThreads) {
8142 uint32_t SrcLocStrSize;
8143 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8144 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8145 Value *ThreadId = getOrCreateThreadID(Ident);
8146 Value *Args[] = {Ident, ThreadId};
8147
8148 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8149 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8150
8151 Function *ExitRTLFn =
8152 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8153 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8154 }
8155
8156 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8157 /*Conditional*/ false, /*hasFinalize*/ true);
8158}
8159
8160OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8161 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8162 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8163 bool HasFinalize, bool IsCancellable) {
8164
8165 if (HasFinalize)
8166 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8167
8168 // Create inlined region's entry and body blocks, in preparation
8169 // for conditional creation
8170 BasicBlock *EntryBB = Builder.GetInsertBlock();
8171 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8173 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8174 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8175 BasicBlock *FiniBB =
8176 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8177
8178 Builder.SetInsertPoint(EntryBB->getTerminator());
8179 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8180
8181 // generate body
8182 if (Error Err =
8183 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8184 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8185 return Err;
8186
8187 // emit exit call and do any needed finalization.
8188 auto FinIP = InsertPointTy(FiniBB, FiniBB->getFirstInsertionPt());
8189 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8190 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8191 "Unexpected control flow graph state!!");
8192 InsertPointOrErrorTy AfterIP =
8193 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8194 if (!AfterIP)
8195 return AfterIP.takeError();
8196
8197 // If we are skipping the region of a non conditional, remove the exit
8198 // block, and clear the builder's insertion point.
8199 assert(SplitPos->getParent() == ExitBB &&
8200 "Unexpected Insertion point location!");
8201 auto merged = MergeBlockIntoPredecessor(ExitBB);
8202 BasicBlock *ExitPredBB = SplitPos->getParent();
8203 auto InsertBB = merged ? ExitPredBB : ExitBB;
8205 SplitPos->eraseFromParent();
8206 Builder.SetInsertPoint(InsertBB);
8207
8208 return Builder.saveIP();
8209}
8210
8211OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8212 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8213 // if nothing to do, Return current insertion point.
8214 if (!Conditional || !EntryCall)
8215 return Builder.saveIP();
8216
8217 BasicBlock *EntryBB = Builder.GetInsertBlock();
8218 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8219 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8220 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8221
8222 // Emit thenBB and set the Builder's insertion point there for
8223 // body generation next. Place the block after the current block.
8224 Function *CurFn = EntryBB->getParent();
8225 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8226
8227 // Move Entry branch to end of ThenBB, and replace with conditional
8228 // branch (If-stmt)
8229 Instruction *EntryBBTI = EntryBB->getTerminator();
8230 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8231 EntryBBTI->removeFromParent();
8232 Builder.SetInsertPoint(UI);
8233 Builder.Insert(EntryBBTI);
8234 UI->eraseFromParent();
8235 Builder.SetInsertPoint(ThenBB->getTerminator());
8236
8237 // return an insertion point to ExitBB.
8238 return IRBuilder<>::InsertPoint(ExitBB, ExitBB->getFirstInsertionPt());
8239}
8240
8241OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8242 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8243 bool HasFinalize) {
8244
8245 Builder.restoreIP(FinIP);
8246
8247 // If there is finalization to do, emit it before the exit call
8248 if (HasFinalize) {
8249 assert(!FinalizationStack.empty() &&
8250 "Unexpected finalization stack state!");
8251
8252 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8253 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8254
8255 if (Error Err = Fi.mergeFiniBB(Builder, FinIP.getBlock()))
8256 return std::move(Err);
8257
8258 // Exit condition: insertion point is before the terminator of the new Fini
8259 // block
8260 Builder.SetInsertPoint(FinIP.getBlock()->getTerminator());
8261 }
8262
8263 if (!ExitCall)
8264 return Builder.saveIP();
8265
8266 // place the Exitcall as last instruction before Finalization block terminator
8267 ExitCall->removeFromParent();
8268 Builder.Insert(ExitCall);
8269
8270 return IRBuilder<>::InsertPoint(ExitCall->getParent(),
8271 ExitCall->getIterator());
8272}
8273
8275 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8276 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8277 if (!IP.isSet())
8278 return IP;
8279
8281
8282 // creates the following CFG structure
8283 // OMP_Entry : (MasterAddr != PrivateAddr)?
8284 // F T
8285 // | \
8286 // | copin.not.master
8287 // | /
8288 // v /
8289 // copyin.not.master.end
8290 // |
8291 // v
8292 // OMP.Entry.Next
8293
8294 BasicBlock *OMP_Entry = IP.getBlock();
8295 Function *CurFn = OMP_Entry->getParent();
8296 BasicBlock *CopyBegin =
8297 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8298 BasicBlock *CopyEnd = nullptr;
8299
8300 // If entry block is terminated, split to preserve the branch to following
8301 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8303 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8304 "copyin.not.master.end");
8305 OMP_Entry->getTerminator()->eraseFromParent();
8306 } else {
8307 CopyEnd =
8308 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8309 }
8310
8311 Builder.SetInsertPoint(OMP_Entry);
8312 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8313 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8314 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8315 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8316
8317 Builder.SetInsertPoint(CopyBegin);
8318 if (BranchtoEnd)
8319 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8320
8321 return Builder.saveIP();
8322}
8323
8325 Value *Size, Value *Allocator,
8326 std::string Name) {
8328 if (!updateToLocation(Loc))
8329 return nullptr;
8330
8331 uint32_t SrcLocStrSize;
8332 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8333 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8334 Value *ThreadId = getOrCreateThreadID(Ident);
8335 Value *Args[] = {ThreadId, Size, Allocator};
8336
8337 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8338
8339 return createRuntimeFunctionCall(Fn, Args, Name);
8340}
8341
8343 Value *Align, Value *Size,
8344 Value *Allocator,
8345 std::string Name) {
8347 if (!updateToLocation(Loc))
8348 return nullptr;
8349
8350 uint32_t SrcLocStrSize;
8351 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8352 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8353 Value *ThreadId = getOrCreateThreadID(Ident);
8354 Value *Args[] = {ThreadId, Align, Size, Allocator};
8355
8356 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8357
8358 return Builder.CreateCall(Fn, Args, Name);
8359}
8360
8362 Value *Addr, Value *Allocator,
8363 std::string Name) {
8365 if (!updateToLocation(Loc))
8366 return nullptr;
8367
8368 uint32_t SrcLocStrSize;
8369 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8370 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8371 Value *ThreadId = getOrCreateThreadID(Ident);
8372 Value *Args[] = {ThreadId, Addr, Allocator};
8373 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8374 return createRuntimeFunctionCall(Fn, Args, Name);
8375}
8376
8378 Value *Size,
8379 const Twine &Name) {
8382
8383 Value *Args[] = {Size};
8384 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8385 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8387 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8388 return Call;
8389}
8390
8392 Type *VarType,
8393 const Twine &Name) {
8394 return createOMPAllocShared(
8395 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8396}
8397
8399 Value *Addr, Value *Size,
8400 const Twine &Name) {
8403
8404 Value *Args[] = {Addr, Size};
8405 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8406 return Builder.CreateCall(Fn, Args, Name);
8407}
8408
8410 Value *Addr, Type *VarType,
8411 const Twine &Name) {
8412 return createOMPFreeShared(
8413 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8414 Name);
8415}
8416
8418 const LocationDescription &Loc, Value *InteropVar,
8419 omp::OMPInteropType InteropType, Value *Device, Value *NumDependences,
8420 Value *DependenceAddress, bool HaveNowaitClause) {
8423
8424 uint32_t SrcLocStrSize;
8425 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8426 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8427 Value *ThreadId = getOrCreateThreadID(Ident);
8428 if (Device == nullptr)
8430 else if (Device->getType() != Int32)
8431 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8432 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8433 if (NumDependences == nullptr) {
8434 NumDependences = ConstantInt::get(Int32, 0);
8435 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8436 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8437 }
8438 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8439 Value *Args[] = {
8440 Ident, ThreadId, InteropVar, InteropTypeVal,
8441 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8442
8443 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8444
8445 return createRuntimeFunctionCall(Fn, Args);
8446}
8447
8449 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8450 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8453
8454 uint32_t SrcLocStrSize;
8455 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8456 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8457 Value *ThreadId = getOrCreateThreadID(Ident);
8458 if (Device == nullptr)
8460 else if (Device->getType() != Int32)
8461 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8462 if (NumDependences == nullptr) {
8463 NumDependences = ConstantInt::get(Int32, 0);
8464 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8465 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8466 }
8467 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8468 Value *Args[] = {
8469 Ident, ThreadId, InteropVar, Device,
8470 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8471
8472 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8473
8474 return createRuntimeFunctionCall(Fn, Args);
8475}
8476
8478 Value *InteropVar, Value *Device,
8479 Value *NumDependences,
8480 Value *DependenceAddress,
8481 bool HaveNowaitClause) {
8484 uint32_t SrcLocStrSize;
8485 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8486 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8487 Value *ThreadId = getOrCreateThreadID(Ident);
8488 if (Device == nullptr)
8490 else if (Device->getType() != Int32)
8491 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8492 if (NumDependences == nullptr) {
8493 NumDependences = ConstantInt::get(Int32, 0);
8494 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8495 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8496 }
8497 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8498 Value *Args[] = {
8499 Ident, ThreadId, InteropVar, Device,
8500 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8501
8502 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8503
8504 return createRuntimeFunctionCall(Fn, Args);
8505}
8506
8509 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8512
8513 uint32_t SrcLocStrSize;
8514 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8515 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8516 Value *ThreadId = getOrCreateThreadID(Ident);
8517 Constant *ThreadPrivateCache =
8518 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8519 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8520
8521 Function *Fn =
8522 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8523
8524 return createRuntimeFunctionCall(Fn, Args);
8525}
8526
8528 const LocationDescription &Loc,
8530 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8531 "expected num_threads and num_teams to be specified");
8532
8533 if (!updateToLocation(Loc))
8534 return Loc.IP;
8535
8536 uint32_t SrcLocStrSize;
8537 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8538 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8539 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8540 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8541 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8542 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8543 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8544 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8545
8546 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8547 Function *Kernel = DebugKernelWrapper;
8548
8549 // We need to strip the debug prefix to get the correct kernel name.
8550 StringRef KernelName = Kernel->getName();
8551 const std::string DebugPrefix = "_debug__";
8552 if (KernelName.ends_with(DebugPrefix)) {
8553 KernelName = KernelName.drop_back(DebugPrefix.length());
8554 Kernel = M.getFunction(KernelName);
8555 assert(Kernel && "Expected the real kernel to exist");
8556 }
8557
8558 // Manifest the launch configuration in the metadata matching the kernel
8559 // environment.
8560 if (Attrs.MinTeams > 1 || Attrs.MaxTeams.front() > 0)
8561 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams, Attrs.MaxTeams.front());
8562
8563 // If MaxThreads is not set and needs adjustment, select the maximum between
8564 // the default workgroup size and the MinThreads value.
8565 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8566 if (MaxThreadsVal < 0 && UseDefaultMaxThreads) {
8567 if (hasGridValue(T)) {
8568 MaxThreadsVal =
8569 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8570 Attrs.MinThreads);
8571 } else {
8572 MaxThreadsVal = Attrs.MinThreads;
8573 }
8574 }
8575
8576 if (MaxThreadsVal > 0)
8577 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads, MaxThreadsVal);
8578
8579 Constant *MinThreads = ConstantInt::getSigned(Int32, Attrs.MinThreads);
8580 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8581 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams);
8582 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8583 Constant *ReductionDataSize =
8584 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8585
8587 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8588 const DataLayout &DL = Fn->getDataLayout();
8589
8590 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8591 Constant *DynamicEnvironmentInitializer =
8592 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8593 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8594 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8595 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8596 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8597 DL.getDefaultGlobalsAddressSpace());
8598 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8599
8600 Constant *DynamicEnvironment =
8601 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8602 ? DynamicEnvironmentGV
8603 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8604 DynamicEnvironmentPtr);
8605
8606 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8607 ConfigurationEnvironment, {
8608 UseGenericStateMachineVal,
8609 MayUseNestedParallelismVal,
8610 IsSPMDVal,
8611 MinThreads,
8612 MaxThreads,
8613 MinTeams,
8614 MaxTeams,
8615 ReductionDataSize,
8616 });
8617 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8618 KernelEnvironment, {
8619 ConfigurationEnvironmentInitializer,
8620 Ident,
8621 DynamicEnvironment,
8622 });
8623 std::string KernelEnvironmentName =
8624 (KernelName + "_kernel_environment").str();
8625 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8626 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8627 KernelEnvironmentInitializer, KernelEnvironmentName,
8628 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8629 DL.getDefaultGlobalsAddressSpace());
8630 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8631
8632 Constant *KernelEnvironment =
8633 KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8634 ? KernelEnvironmentGV
8635 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8636 KernelEnvironmentPtr);
8637 Value *KernelLaunchEnvironment =
8638 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8639 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8640 KernelLaunchEnvironment =
8641 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8642 ? KernelLaunchEnvironment
8643 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8644 KernelLaunchEnvParamTy);
8645 CallInst *ThreadKind = createRuntimeFunctionCall(
8646 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8647
8648 Value *ExecUserCode = Builder.CreateICmpEQ(
8649 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8650 "exec_user_code");
8651
8652 // ThreadKind = __kmpc_target_init(...)
8653 // if (ThreadKind == -1)
8654 // user_code
8655 // else
8656 // return;
8657
8658 auto *UI = Builder.CreateUnreachable();
8659 BasicBlock *CheckBB = UI->getParent();
8660 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8661
8662 BasicBlock *WorkerExitBB = BasicBlock::Create(
8663 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8664 Builder.SetInsertPoint(WorkerExitBB);
8665 Builder.CreateRetVoid();
8666
8667 auto *CheckBBTI = CheckBB->getTerminator();
8668 Builder.SetInsertPoint(CheckBBTI);
8669 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8670
8671 CheckBBTI->eraseFromParent();
8672 UI->eraseFromParent();
8673
8674 // Continue in the "user_code" block, see diagram above and in
8675 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8676 return InsertPointTy(UserCodeEntryBB, UserCodeEntryBB->getFirstInsertionPt());
8677}
8678
8680 int32_t TeamsReductionDataSize) {
8681 if (!updateToLocation(Loc))
8682 return;
8683
8685 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8686
8688
8689 if (!TeamsReductionDataSize)
8690 return;
8691
8692 Function *Kernel = Builder.GetInsertBlock()->getParent();
8693 // We need to strip the debug prefix to get the correct kernel name.
8694 StringRef KernelName = Kernel->getName();
8695 const std::string DebugPrefix = "_debug__";
8696 if (KernelName.ends_with(DebugPrefix))
8697 KernelName = KernelName.drop_back(DebugPrefix.length());
8698 auto *KernelEnvironmentGV =
8699 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8700 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8701 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8702 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8703 KernelEnvironmentInitializer,
8704 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8705 KernelEnvironmentGV->setInitializer(NewInitializer);
8706}
8707
8708static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8709 bool Min) {
8710 if (Kernel.hasFnAttribute(Name)) {
8711 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8712 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8713 }
8714 Kernel.addFnAttr(Name, llvm::utostr(Value));
8715}
8716
8717std::pair<int32_t, int32_t>
8719 int32_t ThreadLimit =
8720 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8721
8722 if (T.isAMDGPU()) {
8723 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8724 if (!Attr.isValid() || !Attr.isStringAttribute())
8725 return {0, ThreadLimit};
8726 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8727 int32_t LB, UB;
8728 if (!llvm::to_integer(UBStr, UB, 10))
8729 return {0, ThreadLimit};
8730 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8731 if (!llvm::to_integer(LBStr, LB, 10))
8732 return {0, UB};
8733 return {LB, UB};
8734 }
8735
8736 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8737 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8738 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8739 }
8740 return {0, ThreadLimit};
8741}
8742
8744 Function &Kernel, int32_t LB,
8745 int32_t UB) {
8746 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8747
8748 if (T.isAMDGPU()) {
8749 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8750 llvm::utostr(LB) + "," + llvm::utostr(UB));
8751 return;
8752 }
8753
8755}
8756
8757std::pair<int32_t, int32_t>
8759 // TODO: Read from backend annotations if available.
8760 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8761}
8762
8764 int32_t LB, int32_t UB) {
8765 if (UB > 0) {
8766 if (T.isNVPTX())
8768 if (T.isAMDGPU())
8769 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8770 }
8771
8772 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8773}
8774
8775void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8776 Function *OutlinedFn) {
8777 if (Config.isTargetDevice()) {
8779 // TODO: Determine if DSO local can be set to true.
8780 OutlinedFn->setDSOLocal(false);
8782 if (T.isAMDGCN())
8784 else if (T.isNVPTX())
8786 else if (T.isSPIRV())
8788 }
8789}
8790
8791Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8792 StringRef EntryFnIDName) {
8793 if (Config.isTargetDevice()) {
8794 assert(OutlinedFn && "The outlined function must exist if embedded");
8795 return OutlinedFn;
8796 }
8797
8798 return new GlobalVariable(
8799 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8800 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8801}
8802
8803Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8804 StringRef EntryFnName) {
8805 if (OutlinedFn)
8806 return OutlinedFn;
8807
8808 assert(!M.getGlobalVariable(EntryFnName, true) &&
8809 "Named kernel already exists?");
8810 return new GlobalVariable(
8811 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8812 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8813}
8814
8816 TargetRegionEntryInfo &EntryInfo,
8817 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8818 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8819
8820 SmallString<64> EntryFnName;
8821 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8822
8823 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8824 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8825 if (!CBResult)
8826 return CBResult.takeError();
8827 OutlinedFn = *CBResult;
8828 } else {
8829 OutlinedFn = nullptr;
8830 }
8831
8832 // If this target outline function is not an offload entry, we don't need to
8833 // register it. This may be in the case of a false if clause, or if there are
8834 // no OpenMP targets.
8835 if (!IsOffloadEntry)
8836 return Error::success();
8837
8838 std::string EntryFnIDName =
8839 Config.isTargetDevice()
8840 ? std::string(EntryFnName)
8841 : createPlatformSpecificName({EntryFnName, "region_id"});
8842
8843 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8844 EntryFnName, EntryFnIDName);
8845 return Error::success();
8846}
8847
8849 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8850 StringRef EntryFnName, StringRef EntryFnIDName) {
8851 if (OutlinedFn)
8852 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8853 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8854 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8855 OffloadInfoManager.registerTargetRegionEntryInfo(
8856 EntryInfo, EntryAddr, OutlinedFnID,
8858 return OutlinedFnID;
8859}
8860
8862 const LocationDescription &Loc, InsertPointTy AllocaIP,
8863 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8864 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8865 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8866 omp::RuntimeFunction *MapperFunc,
8868 BodyGenTy BodyGenType)>
8869 BodyGenCB,
8870 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8871 if (!updateToLocation(Loc))
8872 return InsertPointTy();
8873
8874 Builder.restoreIP(CodeGenIP);
8875
8876 bool IsStandAlone = !BodyGenCB;
8877 MapInfosTy *MapInfo;
8878 // Generate the code for the opening of the data environment. Capture all the
8879 // arguments of the runtime call by reference because they are used in the
8880 // closing of the region.
8881 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8882 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8883 MapInfo = &GenMapInfoCB(Builder.saveIP());
8884 if (Error Err = emitOffloadingArrays(
8885 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
8886 /*IsNonContiguous=*/true, DeviceAddrCB))
8887 return Err;
8888
8889 TargetDataRTArgs RTArgs;
8891
8892 // Emit the number of elements in the offloading arrays.
8893 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8894
8895 // Source location for the ident struct
8896 if (!SrcLocInfo) {
8897 uint32_t SrcLocStrSize;
8898 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8899 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8900 }
8901
8902 SmallVector<llvm::Value *, 13> OffloadingArgs = {
8903 SrcLocInfo, DeviceID,
8904 PointerNum, RTArgs.BasePointersArray,
8905 RTArgs.PointersArray, RTArgs.SizesArray,
8906 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8907 RTArgs.MappersArray};
8908
8909 if (IsStandAlone) {
8910 assert(MapperFunc && "MapperFunc missing for standalone target data");
8911
8912 auto TaskBodyCB = [&](Value *, Value *,
8914 if (Info.HasNoWait) {
8915 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
8919 }
8920
8922 OffloadingArgs);
8923
8924 if (Info.HasNoWait) {
8925 BasicBlock *OffloadContBlock =
8926 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
8927 Function *CurFn = Builder.GetInsertBlock()->getParent();
8928 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
8929 Builder.restoreIP(Builder.saveIP());
8930 }
8931 return Error::success();
8932 };
8933
8934 bool RequiresOuterTargetTask = Info.HasNoWait;
8935 if (!RequiresOuterTargetTask)
8936 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
8937 /*TargetTaskAllocaIP=*/{}));
8938 else
8939 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
8940 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
8941 } else {
8942 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
8943 omp::OMPRTL___tgt_target_data_begin_mapper);
8944
8945 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
8946
8947 for (auto DeviceMap : Info.DevicePtrInfoMap) {
8948 if (isa<AllocaInst>(DeviceMap.second.second)) {
8949 auto *LI =
8950 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
8951 Builder.CreateStore(LI, DeviceMap.second.second);
8952 }
8953 }
8954
8955 // If device pointer privatization is required, emit the body of the
8956 // region here. It will have to be duplicated: with and without
8957 // privatization.
8958 InsertPointOrErrorTy AfterIP =
8959 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
8960 if (!AfterIP)
8961 return AfterIP.takeError();
8962 Builder.restoreIP(*AfterIP);
8963 }
8964 return Error::success();
8965 };
8966
8967 // If we need device pointer privatization, we need to emit the body of the
8968 // region with no privatization in the 'else' branch of the conditional.
8969 // Otherwise, we don't have to do anything.
8970 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8971 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8972 InsertPointOrErrorTy AfterIP =
8973 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
8974 if (!AfterIP)
8975 return AfterIP.takeError();
8976 Builder.restoreIP(*AfterIP);
8977 return Error::success();
8978 };
8979
8980 // Generate code for the closing of the data region.
8981 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8982 ArrayRef<BasicBlock *> DeallocBlocks) {
8983 TargetDataRTArgs RTArgs;
8984 Info.EmitDebug = !MapInfo->Names.empty();
8985 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
8986
8987 // Emit the number of elements in the offloading arrays.
8988 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8989
8990 // Source location for the ident struct
8991 if (!SrcLocInfo) {
8992 uint32_t SrcLocStrSize;
8993 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8994 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8995 }
8996
8997 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
8998 PointerNum, RTArgs.BasePointersArray,
8999 RTArgs.PointersArray, RTArgs.SizesArray,
9000 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9001 RTArgs.MappersArray};
9002 Function *EndMapperFunc =
9003 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9004
9005 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9006 return Error::success();
9007 };
9008
9009 // We don't have to do anything to close the region if the if clause evaluates
9010 // to false.
9011 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9012 ArrayRef<BasicBlock *> DeallocBlocks) {
9013 return Error::success();
9014 };
9015
9016 Error Err = [&]() -> Error {
9017 if (BodyGenCB) {
9018 Error Err = [&]() {
9019 if (IfCond)
9020 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9021 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9022 }();
9023
9024 if (Err)
9025 return Err;
9026
9027 // If we don't require privatization of device pointers, we emit the body
9028 // in between the runtime calls. This avoids duplicating the body code.
9029 InsertPointOrErrorTy AfterIP =
9030 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9031 if (!AfterIP)
9032 return AfterIP.takeError();
9033 restoreIPandDebugLoc(Builder, *AfterIP);
9034
9035 if (IfCond)
9036 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9037 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9038 }
9039 if (IfCond)
9040 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9041 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9042 }();
9043
9044 if (Err)
9045 return Err;
9046
9047 return Builder.saveIP();
9048}
9049
9052 bool IsGPUDistribute) {
9053 assert((IVSize == 32 || IVSize == 64) &&
9054 "IV size is not compatible with the omp runtime");
9055 RuntimeFunction Name;
9056 if (IsGPUDistribute)
9057 Name = IVSize == 32
9058 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9059 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9060 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9061 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9062 else
9063 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9064 : omp::OMPRTL___kmpc_for_static_init_4u)
9065 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9066 : omp::OMPRTL___kmpc_for_static_init_8u);
9067
9068 return getOrCreateRuntimeFunction(M, Name);
9069}
9070
9072 bool IVSigned) {
9073 assert((IVSize == 32 || IVSize == 64) &&
9074 "IV size is not compatible with the omp runtime");
9075 RuntimeFunction Name = IVSize == 32
9076 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9077 : omp::OMPRTL___kmpc_dispatch_init_4u)
9078 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9079 : omp::OMPRTL___kmpc_dispatch_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_next_4
9090 : omp::OMPRTL___kmpc_dispatch_next_4u)
9091 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9092 : omp::OMPRTL___kmpc_dispatch_next_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_fini_4
9103 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9104 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9105 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9106
9107 return getOrCreateRuntimeFunction(M, Name);
9108}
9109
9111 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9112}
9113
9115 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9116 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9117
9118 DISubprogram *NewSP = Func->getSubprogram();
9119 if (!NewSP)
9120 return;
9121
9123
9124 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9125 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9126 // Only use cached variable if the arg number matches. This is important
9127 // so that DIVariable created for privatized variables are not discarded.
9128 if (NewVar && (arg == NewVar->getArg()))
9129 return NewVar;
9130
9132 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9133 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9134 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9135 return NewVar;
9136 };
9137
9138 auto UpdateDebugRecord = [&](auto *DR) {
9139 DILocalVariable *OldVar = DR->getVariable();
9140 unsigned ArgNo = 0;
9141 for (auto Loc : DR->location_ops()) {
9142 auto Iter = ValueReplacementMap.find(Loc);
9143 if (Iter != ValueReplacementMap.end()) {
9144 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9145 ArgNo = std::get<1>(Iter->second) + 1;
9146 }
9147 }
9148 if (ArgNo != 0)
9149 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9150 };
9151
9153 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9154 if (DVR->getNumVariableLocationOps() != 1u) {
9155 DVR->setKillLocation();
9156 return;
9157 }
9158 Value *Loc = DVR->getVariableLocationOp(0u);
9159 BasicBlock *CurBB = DVR->getParent();
9160 BasicBlock *RequiredBB = nullptr;
9161
9162 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9163 RequiredBB = LocInst->getParent();
9164 else if (isa<llvm::Argument>(Loc))
9165 RequiredBB = &DVR->getFunction()->getEntryBlock();
9166
9167 if (RequiredBB && RequiredBB != CurBB) {
9168 assert(!RequiredBB->empty());
9169 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9170 RequiredBB->back().getIterator());
9171 DVRsToDelete.push_back(DVR);
9172 }
9173 };
9174
9175 // The location and scope of variable intrinsics and records still point to
9176 // the parent function of the target region. Update them.
9177 for (Instruction &I : instructions(Func)) {
9179 "Unexpected debug intrinsic");
9180 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9181 UpdateDebugRecord(&DVR);
9182 MoveDebugRecordToCorrectBlock(&DVR);
9183 }
9184 }
9185 for (auto *DVR : DVRsToDelete)
9186 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9187 // An extra argument is passed to the device. Create the debug data for it.
9188 if (OMPBuilder.Config.isTargetDevice()) {
9189 DICompileUnit *CU = NewSP->getUnit();
9190 Module *M = Func->getParent();
9191 DIBuilder DB(*M, true, CU);
9192 DIType *VoidPtrTy =
9193 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9194 unsigned ArgNo = Func->arg_size();
9195 DILocalVariable *Var = DB.createParameterVariable(
9196 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9197 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9198 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9199 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9200 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9201 &(*Func->begin()));
9202 }
9203}
9204
9206 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9207 return cast<Operator>(V)->getOperand(0);
9208 return V;
9209}
9210
9212 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9214 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9217 SmallVector<Type *> ParameterTypes;
9218 if (OMPBuilder.Config.isTargetDevice()) {
9219 // All parameters to target devices are passed as pointers
9220 // or i64. This assumes 64-bit address spaces/pointers.
9221 for (auto &Arg : Inputs)
9222 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9223 ? Arg->getType()
9224 : Type::getInt64Ty(Builder.getContext()));
9225 } else {
9226 for (auto &Arg : Inputs)
9227 ParameterTypes.push_back(Arg->getType());
9228 }
9229
9230 // The implicit dyn_ptr argument is always the last parameter on both host
9231 // and device so the argument counts match without runtime manipulation.
9232 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9233 ParameterTypes.push_back(PtrTy);
9234
9235 auto BB = Builder.GetInsertBlock();
9236 auto M = BB->getModule();
9237 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9238 /*isVarArg*/ false);
9239 auto Func =
9240 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9241
9242 // Forward target-cpu and target-features function attributes from the
9243 // original function to the new outlined function.
9244 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9245
9246 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9247 if (TargetCpuAttr.isStringAttribute())
9248 Func->addFnAttr(TargetCpuAttr);
9249
9250 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9251 if (TargetFeaturesAttr.isStringAttribute())
9252 Func->addFnAttr(TargetFeaturesAttr);
9253
9254 if (OMPBuilder.Config.isTargetDevice()) {
9255 Value *ExecMode =
9256 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9257 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9258 }
9259
9260 // Save insert point.
9261 IRBuilder<>::InsertPointGuard IPG(Builder);
9262 // We will generate the entries in the outlined function but the debug
9263 // location may still be pointing to the parent function. Reset it now.
9264 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9265
9266 // Generate the region into the function.
9267 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9268 Builder.SetInsertPoint(EntryBB);
9269
9270 // Insert target init call in the device compilation pass.
9271 if (OMPBuilder.Config.isTargetDevice())
9272 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9273
9274 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9275
9276 // As we embed the user code in the middle of our target region after we
9277 // generate entry code, we must move what allocas we can into the entry
9278 // block to avoid possible breaking optimisations for device
9279 if (OMPBuilder.Config.isTargetDevice())
9281
9282 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9283 BasicBlock *OutlinedBodyBB =
9284 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9286 Builder.saveIP(),
9287 OpenMPIRBuilder::InsertPointTy(OutlinedBodyBB, OutlinedBodyBB->begin()),
9288 ExitBB);
9289 if (!AfterIP)
9290 return AfterIP.takeError();
9291 Builder.SetInsertPoint(ExitBB);
9292
9293 // Insert target deinit call in the device compilation pass.
9294 if (OMPBuilder.Config.isTargetDevice())
9295 OMPBuilder.createTargetDeinit(Builder);
9296
9297 // Insert return instruction.
9298 Builder.CreateRetVoid();
9299
9300 // New Alloca IP at entry point of created device function.
9301 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9302 auto AllocaIP = Builder.saveIP();
9303
9304 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());
9305
9306 // Do not include the artificial dyn_ptr argument.
9307 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9308
9310
9311 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9312 // Things like GEP's can come in the form of Constants. Constants and
9313 // ConstantExpr's do not have access to the knowledge of what they're
9314 // contained in, so we must dig a little to find an instruction so we
9315 // can tell if they're used inside of the function we're outlining. We
9316 // also replace the original constant expression with a new instruction
9317 // equivalent; an instruction as it allows easy modification in the
9318 // following loop, as we can now know the constant (instruction) is
9319 // owned by our target function and replaceUsesOfWith can now be invoked
9320 // on it (cannot do this with constants it seems). A brand new one also
9321 // allows us to be cautious as it is perhaps possible the old expression
9322 // was used inside of the function but exists and is used externally
9323 // (unlikely by the nature of a Constant, but still).
9324 // NOTE: We cannot remove dead constants that have been rewritten to
9325 // instructions at this stage, we run the risk of breaking later lowering
9326 // by doing so as we could still be in the process of lowering the module
9327 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9328 // constants we have created rewritten versions of.
9329 if (auto *Const = dyn_cast<Constant>(Input))
9330 convertUsersOfConstantsToInstructions(Const, Func, false);
9331
9332 // Collect users before iterating over them to avoid invalidating the
9333 // iteration in case a user uses Input more than once (e.g. a call
9334 // instruction).
9335 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9336 // Collect all the instructions
9338 if (auto *Instr = dyn_cast<Instruction>(User))
9339 if (Instr->getFunction() == Func)
9340 Instr->replaceUsesOfWith(Input, InputCopy);
9341 };
9342
9343 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9344
9345 // Rewrite uses of input valus to parameters.
9346 for (auto InArg : zip(Inputs, ArgRange)) {
9347 Value *Input = std::get<0>(InArg);
9348 Argument &Arg = std::get<1>(InArg);
9349 Value *InputCopy = nullptr;
9350
9351 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9352 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(),
9353 OpenMPIRBuilder::InsertPointTy(ExitBB, ExitBB->begin()));
9354 if (!AfterIP)
9355 return AfterIP.takeError();
9356 Builder.restoreIP(*AfterIP);
9357 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9358
9359 // In certain cases a Global may be set up for replacement, however, this
9360 // Global may be used in multiple arguments to the kernel, just segmented
9361 // apart, for example, if we have a global array, that is sectioned into
9362 // multiple mappings (technically not legal in OpenMP, but there is a case
9363 // in Fortran for Common Blocks where this is neccesary), we will end up
9364 // with GEP's into this array inside the kernel, that refer to the Global
9365 // but are technically separate arguments to the kernel for all intents and
9366 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9367 // index, it will fold into an referal to the Global, if we then encounter
9368 // this folded GEP during replacement all of the references to the
9369 // Global in the kernel will be replaced with the argument we have generated
9370 // that corresponds to it, including any other GEP's that refer to the
9371 // Global that may be other arguments. This will invalidate all of the other
9372 // preceding mapped arguments that refer to the same global that may be
9373 // separate segments. To prevent this, we defer global processing until all
9374 // other processing has been performed.
9377 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9378 continue;
9379 }
9380
9382 continue;
9383
9384 ReplaceValue(Input, InputCopy, Func);
9385 }
9386
9387 // Replace all of our deferred Input values, currently just Globals.
9388 for (auto Deferred : DeferredReplacement)
9389 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9390
9391 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9392 ValueReplacementMap);
9393 return Func;
9394}
9395/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9396/// of pointers containing shared data between the parent task and the created
9397/// task.
9399 IRBuilderBase &Builder,
9400 Value *TaskWithPrivates,
9401 Type *TaskWithPrivatesTy) {
9402
9403 Type *TaskTy = OMPIRBuilder.Task;
9404 LLVMContext &Ctx = Builder.getContext();
9405 Value *TaskT =
9406 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9407 Value *Shareds = TaskT;
9408 // TaskWithPrivatesTy can be one of the following
9409 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9410 // %struct.privates }
9411 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9412 //
9413 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9414 // its first member has to be the task descriptor. TaskTy is the type of the
9415 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9416 // first member of TaskT, gives us the pointer to shared data.
9417 if (TaskWithPrivatesTy != TaskTy)
9418 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9419 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9420}
9421/// Create an entry point for a target task with the following.
9422/// It'll have the following signature
9423/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9424/// This function is called from emitTargetTask once the
9425/// code to launch the target kernel has been outlined already.
9426/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9427/// into the task structure so that the deferred target task can access this
9428/// data even after the stack frame of the generating task has been rolled
9429/// back. Offloading arrays contain base pointers, pointers, sizes etc
9430/// of the data that the target kernel will access. These in effect are the
9431/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9433 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9434 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9435 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9436
9437 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9438 // This is because PrivatesTy is the type of the structure in which
9439 // we pass the offloading arrays to the deferred target task.
9440 assert((!NumOffloadingArrays || PrivatesTy) &&
9441 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9442 "to privatize");
9443
9444 Module &M = OMPBuilder.M;
9445 // KernelLaunchFunction is the target launch function, i.e.
9446 // the function that sets up kernel arguments and calls
9447 // __tgt_target_kernel to launch the kernel on the device.
9448 //
9449 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9450
9451 // StaleCI is the CallInst which is the call to the outlined
9452 // target kernel launch function. If there are local live-in values
9453 // that the outlined function uses then these are aggregated into a structure
9454 // which is passed as the second argument. If there are no local live-in
9455 // values or if all values used by the outlined kernel are global variables,
9456 // then there's only one argument, the threadID. So, StaleCI can be
9457 //
9458 // %structArg = alloca { ptr, ptr }, align 8
9459 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9460 // store ptr %20, ptr %gep_, align 8
9461 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9462 // store ptr %21, ptr %gep_8, align 8
9463 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9464 //
9465 // OR
9466 //
9467 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9469 StaleCI->getIterator());
9470
9471 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9472
9473 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9474 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9475 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9476
9477 auto ProxyFnTy =
9478 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9479 /* isVarArg */ false);
9480 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9481 ".omp_target_task_proxy_func",
9482 Builder.GetInsertBlock()->getModule());
9483 Value *ThreadId = ProxyFn->getArg(0);
9484 Value *TaskWithPrivates = ProxyFn->getArg(1);
9485 ThreadId->setName("thread.id");
9486 TaskWithPrivates->setName("task");
9487
9488 bool HasShareds = SharedArgsOperandNo > 0;
9489 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9490 BasicBlock *EntryBB =
9491 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9492 Builder.SetInsertPoint(EntryBB);
9493
9494 SmallVector<Value *> KernelLaunchArgs;
9495 KernelLaunchArgs.reserve(StaleCI->arg_size());
9496 KernelLaunchArgs.push_back(ThreadId);
9497
9498 if (HasOffloadingArrays) {
9499 assert(TaskTy != TaskWithPrivatesTy &&
9500 "If there are offloading arrays to pass to the target"
9501 "TaskTy cannot be the same as TaskWithPrivatesTy");
9502 (void)TaskTy;
9503 Value *Privates =
9504 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9505 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9506 KernelLaunchArgs.push_back(
9507 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9508 }
9509
9510 if (HasShareds) {
9511 auto *ArgStructAlloca =
9512 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9513 assert(ArgStructAlloca &&
9514 "Unable to find the alloca instruction corresponding to arguments "
9515 "for extracted function");
9516 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9517 std::optional<TypeSize> ArgAllocSize =
9518 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9519 assert(ArgStructType && ArgAllocSize &&
9520 "Unable to determine size of arguments for extracted function");
9521 uint64_t StructSize = ArgAllocSize->getFixedValue();
9522
9523 AllocaInst *NewArgStructAlloca =
9524 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9525
9526 Value *SharedsSize = Builder.getInt64(StructSize);
9527
9529 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9530
9531 Builder.CreateMemCpy(
9532 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9533 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9534 KernelLaunchArgs.push_back(NewArgStructAlloca);
9535 }
9536 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9537 Builder.CreateRetVoid();
9538 return ProxyFn;
9539}
9541
9542 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9543 return GEP->getSourceElementType();
9544 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9545 return Alloca->getAllocatedType();
9546
9547 llvm_unreachable("Unhandled Instruction type");
9548 return nullptr;
9549}
9550// This function returns a struct that has at most two members.
9551// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9552// descriptor. The second member, if needed, is a struct containing arrays
9553// that need to be passed to the offloaded target kernel. For example,
9554// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9555// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9556// respectively, then the types created by this function are
9557//
9558// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9559// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9560// %struct.privates }
9561// %struct.task_with_privates is returned by this function.
9562// If there aren't any offloading arrays to pass to the target kernel,
9563// %struct.kmp_task_ompbuilder_t is returned.
9564static StructType *
9566 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9567
9568 if (OffloadingArraysToPrivatize.empty())
9569 return OMPIRBuilder.Task;
9570
9571 SmallVector<Type *, 4> StructFieldTypes;
9572 for (Value *V : OffloadingArraysToPrivatize) {
9573 assert(V->getType()->isPointerTy() &&
9574 "Expected pointer to array to privatize. Got a non-pointer value "
9575 "instead");
9576 Type *ArrayTy = getOffloadingArrayType(V);
9577 assert(ArrayTy && "ArrayType cannot be nullptr");
9578 StructFieldTypes.push_back(ArrayTy);
9579 }
9580 StructType *PrivatesStructTy =
9581 StructType::create(StructFieldTypes, "struct.privates");
9582 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9583 "struct.task_with_privates");
9584}
9586 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9587 TargetRegionEntryInfo &EntryInfo,
9589 Function *&OutlinedFn, Constant *&OutlinedFnID,
9593
9594 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9595 [&](StringRef EntryFnName) {
9596 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9597 EntryFnName, Inputs, CBFunc,
9598 ArgAccessorFuncCB);
9599 };
9600
9601 return OMPBuilder.emitTargetRegionFunction(
9602 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9603 OutlinedFnID);
9604}
9605
9607 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9609 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9610 bool HasNoWait) {
9611
9612 // The following explains the code-gen scenario for the `target` directive. A
9613 // similar scneario is followed for other device-related directives (e.g.
9614 // `target enter data`) but in similar fashion since we only need to emit task
9615 // that encapsulates the proper runtime call.
9616 //
9617 // When we arrive at this function, the target region itself has been
9618 // outlined into the function OutlinedFn.
9619 // So at ths point, for
9620 // --------------------------------------------------------------
9621 // void user_code_that_offloads(...) {
9622 // omp target depend(..) map(from:a) map(to:b) private(i)
9623 // do i = 1, 10
9624 // a(i) = b(i) + n
9625 // }
9626 //
9627 // --------------------------------------------------------------
9628 //
9629 // we have
9630 //
9631 // --------------------------------------------------------------
9632 //
9633 // void user_code_that_offloads(...) {
9634 // %.offload_baseptrs = alloca [2 x ptr], align 8
9635 // %.offload_ptrs = alloca [2 x ptr], align 8
9636 // %.offload_mappers = alloca [2 x ptr], align 8
9637 // ;; target region has been outlined and now we need to
9638 // ;; offload to it via a target task.
9639 // }
9640 // void outlined_device_function(ptr a, ptr b, ptr n) {
9641 // n = *n_ptr;
9642 // do i = 1, 10
9643 // a(i) = b(i) + n
9644 // }
9645 //
9646 // We have to now do the following
9647 // (i) Make an offloading call to outlined_device_function using the OpenMP
9648 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9649 // emitted by emitKernelLaunch
9650 // (ii) Create a task entry point function that calls kernel_launch_function
9651 // and is the entry point for the target task. See
9652 // '@.omp_target_task_proxy_func in the pseudocode below.
9653 // (iii) Create a task with the task entry point created in (ii)
9654 //
9655 // That is we create the following
9656 // struct task_with_privates {
9657 // struct kmp_task_ompbuilder_t task_struct;
9658 // struct privates {
9659 // [2 x ptr] ; baseptrs
9660 // [2 x ptr] ; ptrs
9661 // [2 x i64] ; sizes
9662 // }
9663 // }
9664 // void user_code_that_offloads(...) {
9665 // %.offload_baseptrs = alloca [2 x ptr], align 8
9666 // %.offload_ptrs = alloca [2 x ptr], align 8
9667 // %.offload_sizes = alloca [2 x i64], align 8
9668 //
9669 // %structArg = alloca { ptr, ptr, ptr }, align 8
9670 // %strucArg[0] = a
9671 // %strucArg[1] = b
9672 // %strucArg[2] = &n
9673 //
9674 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9675 // sizeof(kmp_task_ompbuilder_t),
9676 // sizeof(structArg),
9677 // @.omp_target_task_proxy_func,
9678 // ...)
9679 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9680 // sizeof(structArg))
9681 // memcpy(target_task_with_privates->privates->baseptrs,
9682 // offload_baseptrs, sizeof(offload_baseptrs)
9683 // memcpy(target_task_with_privates->privates->ptrs,
9684 // offload_ptrs, sizeof(offload_ptrs)
9685 // memcpy(target_task_with_privates->privates->sizes,
9686 // offload_sizes, sizeof(offload_sizes)
9687 // dependencies_array = ...
9688 // ;; if nowait not present
9689 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9690 // call @__kmpc_omp_task_begin_if0(...)
9691 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9692 // %target_task_with_privates)
9693 // call @__kmpc_omp_task_complete_if0(...)
9694 // }
9695 //
9696 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9697 // ptr %task) {
9698 // %structArg = alloca {ptr, ptr, ptr}
9699 // %task_ptr = getelementptr(%task, 0, 0)
9700 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9701 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9702 //
9703 // %offloading_arrays = getelementptr(%task, 0, 1)
9704 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9705 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9706 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9707 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9708 // %offload_sizes, %structArg)
9709 // }
9710 //
9711 // We need the proxy function because the signature of the task entry point
9712 // expected by kmpc_omp_task is always the same and will be different from
9713 // that of the kernel_launch function.
9714 //
9715 // kernel_launch_function is generated by emitKernelLaunch and has the
9716 // always_inline attribute. For this example, it'll look like so:
9717 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9718 // %offload_sizes, %structArg) alwaysinline {
9719 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9720 // ; load aggregated data from %structArg
9721 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9722 // ; offload_sizes
9723 // call i32 @__tgt_target_kernel(...,
9724 // outlined_device_function,
9725 // ptr %kernel_args)
9726 // }
9727 // void outlined_device_function(ptr a, ptr b, ptr n) {
9728 // n = *n_ptr;
9729 // do i = 1, 10
9730 // a(i) = b(i) + n
9731 // }
9732 //
9733 BasicBlock *TargetTaskBodyBB =
9734 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9735 BasicBlock *TargetTaskAllocaBB =
9736 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9737
9738 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB,
9739 TargetTaskAllocaBB->begin());
9740 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB, TargetTaskBodyBB->begin());
9741
9742 auto OI = std::make_unique<OutlineInfo>();
9743 OI->EntryBB = TargetTaskAllocaBB;
9744 OI->OuterAllocBB = AllocaIP.getBlock();
9745
9746 // Add the thread ID argument.
9748 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9749 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9750
9751 // Generate the task body which will subsequently be outlined.
9752 Builder.restoreIP(TargetTaskBodyIP);
9753 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9754 return Err;
9755
9756 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9757 // it is given. These blocks are enumerated by
9758 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9759 // to be outside the region. In other words, OI.ExitBlock is expected to be
9760 // the start of the region after the outlining. We used to set OI.ExitBlock
9761 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9762 // except when the task body is a single basic block. In that case,
9763 // OI.ExitBlock is set to the single task body block and will get left out of
9764 // the outlining process. So, simply create a new empty block to which we
9765 // uncoditionally branch from where TaskBodyCB left off
9766 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9767 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9768 /*IsFinished=*/true);
9769
9770 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9771 bool NeedsTargetTask = HasNoWait && DeviceID;
9772 if (NeedsTargetTask) {
9773 for (auto *V :
9774 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9775 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9776 RTArgs.SizesArray}) {
9778 OffloadingArraysToPrivatize.push_back(V);
9779 OI->ExcludeArgsFromAggregate.push_back(V);
9780 }
9781 }
9782 }
9783 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9784 DeviceID, OffloadingArraysToPrivatize](
9785 Function &OutlinedFn) mutable {
9786 assert(OutlinedFn.hasOneUse() &&
9787 "there must be a single user for the outlined function");
9788
9789 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9790
9791 // The first argument of StaleCI is always the thread id.
9792 // The next few arguments are the pointers to offloading arrays
9793 // if any. (see OffloadingArraysToPrivatize)
9794 // Finally, all other local values that are live-in into the outlined region
9795 // end up in a structure whose pointer is passed as the last argument. This
9796 // piece of data is passed in the "shared" field of the task structure. So,
9797 // we know we have to pass shareds to the task if the number of arguments is
9798 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9799 // thread id. Further, for safety, we assert that the number of arguments of
9800 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9801 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9802 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9803 assert((!HasShareds ||
9804 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9805 "Wrong number of arguments for StaleCI when shareds are present");
9806 int SharedArgOperandNo =
9807 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9808
9809 StructType *TaskWithPrivatesTy =
9810 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9811 StructType *PrivatesTy = nullptr;
9812
9813 if (!OffloadingArraysToPrivatize.empty())
9814 PrivatesTy =
9815 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9816
9818 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9819 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9820
9821 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9822 << "\n");
9823
9824 Builder.SetInsertPoint(StaleCI);
9825
9826 // Gather the arguments for emitting the runtime call.
9827 uint32_t SrcLocStrSize;
9828 Constant *SrcLocStr =
9830 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9831
9832 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9833 //
9834 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9835 // the DeviceID to the deferred task and also since
9836 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9837 Function *TaskAllocFn =
9838 !NeedsTargetTask
9839 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9841 OMPRTL___kmpc_omp_target_task_alloc);
9842
9843 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9844 // call.
9845 Value *ThreadID = getOrCreateThreadID(Ident);
9846
9847 // Argument - `sizeof_kmp_task_t` (TaskSize)
9848 // Tasksize refers to the size in bytes of kmp_task_t data structure
9849 // plus any other data to be passed to the target task, if any, which
9850 // is packed into a struct. kmp_task_t and the struct so created are
9851 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9852 Value *TaskSize = Builder.getInt64(
9853 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9854
9855 // Argument - `sizeof_shareds` (SharedsSize)
9856 // SharedsSize refers to the shareds array size in the kmp_task_t data
9857 // structure.
9858 Value *SharedsSize = Builder.getInt64(0);
9859 if (HasShareds) {
9860 auto *ArgStructAlloca =
9861 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9862 assert(ArgStructAlloca &&
9863 "Unable to find the alloca instruction corresponding to arguments "
9864 "for extracted function");
9865 std::optional<TypeSize> ArgAllocSize =
9866 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9867 assert(ArgAllocSize &&
9868 "Unable to determine size of arguments for extracted function");
9869 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9870 }
9871
9872 // Argument - `flags`
9873 // Task is tied iff (Flags & 1) == 1.
9874 // Task is untied iff (Flags & 1) == 0.
9875 // Task is final iff (Flags & 2) == 2.
9876 // Task is not final iff (Flags & 2) == 0.
9877 // A target task is not final and is untied.
9878 Value *Flags = Builder.getInt32(0);
9879
9880 // Emit the @__kmpc_omp_task_alloc runtime call
9881 // The runtime call returns a pointer to an area where the task captured
9882 // variables must be copied before the task is run (TaskData)
9883 CallInst *TaskData = nullptr;
9884
9885 SmallVector<llvm::Value *> TaskAllocArgs = {
9886 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
9887 /*flags=*/Flags,
9888 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
9889 /*task_func=*/ProxyFn};
9890
9891 if (NeedsTargetTask) {
9892 assert(DeviceID && "Expected non-empty device ID.");
9893 TaskAllocArgs.push_back(DeviceID);
9894 }
9895
9896 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
9897
9898 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
9899 if (HasShareds) {
9900 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
9902 *this, Builder, TaskData, TaskWithPrivatesTy);
9903 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
9904 SharedsSize);
9905 }
9906 if (!OffloadingArraysToPrivatize.empty()) {
9907 Value *Privates =
9908 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
9909 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
9910 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
9911 [[maybe_unused]] Type *ArrayType =
9912 getOffloadingArrayType(PtrToPrivatize);
9913 assert(ArrayType && "ArrayType cannot be nullptr");
9914
9915 Type *ElementType = PrivatesTy->getElementType(i);
9916 assert(ElementType == ArrayType &&
9917 "ElementType should match ArrayType");
9918 (void)ArrayType;
9919
9920 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
9921 Builder.CreateMemCpy(
9922 Dst, Alignment, PtrToPrivatize, Alignment,
9923 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
9924 }
9925 }
9926
9927 Value *DepArray = nullptr;
9928 Value *NumDeps = nullptr;
9929 if (Dependencies.DepArray) {
9930 DepArray = Dependencies.DepArray;
9931 NumDeps = Dependencies.NumDeps;
9932 } else if (!Dependencies.Deps.empty()) {
9933 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
9934 NumDeps = Builder.getInt32(Dependencies.Deps.size());
9935 }
9936
9937 // ---------------------------------------------------------------
9938 // V5.2 13.8 target construct
9939 // If the nowait clause is present, execution of the target task
9940 // may be deferred. If the nowait clause is not present, the target task is
9941 // an included task.
9942 // ---------------------------------------------------------------
9943 // The above means that the lack of a nowait on the target construct
9944 // translates to '#pragma omp task if(0)'
9945 if (!NeedsTargetTask) {
9946 if (DepArray) {
9947 Function *TaskWaitFn =
9948 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
9950 TaskWaitFn,
9951 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
9952 /*ndeps=*/NumDeps,
9953 /*dep_list=*/DepArray,
9954 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
9955 /*noalias_dep_list=*/
9957 }
9958 // Included task.
9959 Function *TaskBeginFn =
9960 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
9961 Function *TaskCompleteFn =
9962 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
9963 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
9964 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
9965 CI->setDebugLoc(StaleCI->getDebugLoc());
9966 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
9967 } else if (DepArray) {
9968 // HasNoWait - meaning the task may be deferred. Call
9969 // __kmpc_omp_task_with_deps if there are dependencies,
9970 // else call __kmpc_omp_task
9971 Function *TaskFn =
9972 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
9974 TaskFn,
9975 {Ident, ThreadID, TaskData, NumDeps, DepArray,
9976 ConstantInt::get(Builder.getInt32Ty(), 0),
9978 } else {
9979 // Emit the @__kmpc_omp_task runtime call to spawn the task
9980 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
9981 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
9982 }
9983
9984 StaleCI->eraseFromParent();
9985 for (Instruction *I : llvm::reverse(ToBeDeleted))
9986 I->eraseFromParent();
9987 };
9988 addOutlineInfo(std::move(OI));
9989
9990 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
9991 << *(Builder.GetInsertBlock()) << "\n");
9992 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
9993 << *(Builder.GetInsertBlock()->getParent()->getParent())
9994 << "\n");
9995 return Builder.saveIP();
9996}
9997
9999 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10000 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10001 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10002 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10003 if (Error Err =
10004 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10005 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10006 return Err;
10007 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10008 return Error::success();
10009}
10010
10011static void emitTargetCall(
10012 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
10017 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10021 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10022 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10023 // Generate a function call to the host fallback implementation of the target
10024 // region. This is called by the host when no offload entry was generated for
10025 // the target region and when the offloading call fails at runtime.
10026 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10028 Builder.restoreIP(IP);
10029 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10030 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10031 FallbackArgs.push_back(
10032 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10033 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10034 return Builder.saveIP();
10035 };
10036
10037 bool HasDependencies = !Dependencies.empty();
10038 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10039
10041
10042 auto TaskBodyCB =
10043 [&](Value *DeviceID, Value *RTLoc,
10044 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10045 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10046 // produce any.
10048 // emitKernelLaunch makes the necessary runtime call to offload the
10049 // kernel. We then outline all that code into a separate function
10050 // ('kernel_launch_function' in the pseudo code above). This function is
10051 // then called by the target task proxy function (see
10052 // '@.omp_target_task_proxy_func' in the pseudo code above)
10053 // "@.omp_target_task_proxy_func' is generated by
10054 // emitTargetTaskProxyFunction.
10055 if (OutlinedFnID && DeviceID)
10056 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10057 EmitTargetCallFallbackCB, KArgs,
10058 DeviceID, RTLoc, TargetTaskAllocaIP);
10059
10060 // We only need to do the outlining if `DeviceID` is set to avoid calling
10061 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10062 // generating the `else` branch of an `if` clause.
10063 //
10064 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10065 // In this case, we execute the host implementation directly.
10066 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10067 }());
10068
10069 OMPBuilder.Builder.restoreIP(AfterIP);
10070 return Error::success();
10071 };
10072
10073 auto &&EmitTargetCallElse =
10074 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10076 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10077 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10078 // produce any.
10080 if (RequiresOuterTargetTask) {
10081 // Arguments that are intended to be directly forwarded to an
10082 // emitKernelLaunch call are pased as nullptr, since
10083 // OutlinedFnID=nullptr results in that call not being done.
10085 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10086 /*RTLoc=*/nullptr, AllocaIP,
10087 Dependencies, EmptyRTArgs, HasNoWait);
10088 }
10089 return EmitTargetCallFallbackCB(Builder.saveIP());
10090 }());
10091
10092 Builder.restoreIP(AfterIP);
10093 return Error::success();
10094 };
10095
10096 auto &&EmitTargetCallThen =
10097 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10099 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10100 Info.HasNoWait = HasNoWait;
10101 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10102
10104 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10105 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10106 /*IsNonContiguous=*/true,
10107 /*ForEndCall=*/false))
10108 return Err;
10109
10110 SmallVector<Value *, 3> NumTeamsC;
10111 for (auto [DefaultVal, RuntimeVal] :
10112 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10113 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10114 : Builder.getInt32(DefaultVal));
10115
10116 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10117 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10118 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10119 if (Clause)
10120 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10121 /*isSigned=*/false);
10122 return Clause;
10123 };
10124 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10125 if (Clause)
10126 Result =
10127 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10128 Result, Clause)
10129 : Clause;
10130 };
10131
10132 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10133 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10134 SmallVector<Value *, 3> NumThreadsC;
10135 Value *MaxThreadsClause =
10136 RuntimeAttrs.TeamsThreadLimit.size() == 1
10137 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads)
10138 : nullptr;
10139
10140 for (auto [TeamsVal, TargetVal] : zip_equal(
10141 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10142 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10143 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10144
10145 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10146 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10147
10148 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10149 }
10150
10151 unsigned NumTargetItems = Info.NumberOfPtrs;
10152 uint32_t SrcLocStrSize;
10153 Constant *SrcLocStr = OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10154 Value *RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10155 llvm::omp::IdentFlag(0), 0);
10156
10157 Value *TripCount = RuntimeAttrs.LoopTripCount
10158 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10159 Builder.getInt64Ty(),
10160 /*isSigned=*/false)
10161 : Builder.getInt64(0);
10162
10163 // Request zero groupprivate bytes by default.
10164 if (!DynCGroupMem)
10165 DynCGroupMem = Builder.getInt32(0);
10166
10168 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10169 HasNoWait, /*StrictBlocksAndThreads=*/false, DynCGroupMemFallback);
10170
10171 // Assume no error was returned because TaskBodyCB and
10172 // EmitTargetCallFallbackCB don't produce any.
10174 // The presence of certain clauses on the target directive require the
10175 // explicit generation of the target task.
10176 if (RequiresOuterTargetTask)
10177 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10178 RTLoc, AllocaIP, Dependencies,
10179 KArgs.RTArgs, Info.HasNoWait);
10180
10181 return OMPBuilder.emitKernelLaunch(
10182 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10183 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10184 }());
10185
10186 Builder.restoreIP(AfterIP);
10187 return Error::success();
10188 };
10189
10190 // If we don't have an ID for the target region, it means an offload entry
10191 // wasn't created. In this case we just run the host fallback directly and
10192 // ignore any potential 'if' clauses.
10193 if (!OutlinedFnID) {
10194 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10195 return;
10196 }
10197
10198 // If there's no 'if' clause, only generate the kernel launch code path.
10199 if (!IfCond) {
10200 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10201 return;
10202 }
10203
10204 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10205 EmitTargetCallElse, AllocaIP));
10206}
10207
10209 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10210 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10211 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10212 const TargetKernelDefaultAttrs &DefaultAttrs,
10213 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10214 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10217 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10218 bool HasNowait, Value *DynCGroupMem,
10219 OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10220
10221 if (!updateToLocation(Loc))
10222 return InsertPointTy();
10223
10224 Builder.restoreIP(CodeGenIP);
10225
10226 Function *OutlinedFn;
10227 Constant *OutlinedFnID = nullptr;
10228 // The target region is outlined into its own function. The LLVM IR for
10229 // the target region itself is generated using the callbacks CBFunc
10230 // and ArgAccessorFuncCB
10232 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10233 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB))
10234 return Err;
10235
10236 // If we are not on the target device, then we need to generate code
10237 // to make a remote call (offload) to the previously outlined function
10238 // that represents the target region. Do that now.
10239 if (!Config.isTargetDevice())
10240 emitTargetCall(*this, Builder, AllocaIP, DeallocBlocks, Info, DefaultAttrs,
10241 RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID, Inputs,
10242 GenMapInfoCB, CustomMapperCB, Dependencies, HasNowait,
10243 DynCGroupMem, DynCGroupMemFallback);
10244 return Builder.saveIP();
10245}
10246
10247std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10248 StringRef FirstSeparator,
10249 StringRef Separator) {
10250 SmallString<128> Buffer;
10251 llvm::raw_svector_ostream OS(Buffer);
10252 StringRef Sep = FirstSeparator;
10253 for (StringRef Part : Parts) {
10254 OS << Sep << Part;
10255 Sep = Separator;
10256 }
10257 return OS.str().str();
10258}
10259
10260std::string
10262 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10263 Config.separator());
10264}
10265
10267 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10268 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10269 if (Elem.second) {
10270 assert(Elem.second->getValueType() == Ty &&
10271 "OMP internal variable has different type than requested");
10272 } else {
10273 // TODO: investigate the appropriate linkage type used for the global
10274 // variable for possibly changing that to internal or private, or maybe
10275 // create different versions of the function for different OMP internal
10276 // variables.
10277 const DataLayout &DL = M.getDataLayout();
10278 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10279 // default global AS is 1.
10280 // See double-target-call-with-declare-target.f90 and
10281 // declare-target-vars-in-target-region.f90 libomptarget
10282 // tests.
10283 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10284 : M.getTargetTriple().isAMDGPU()
10285 ? 0
10286 : DL.getDefaultGlobalsAddressSpace();
10287 auto Linkage = this->M.getTargetTriple().isWasm()
10290 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10291 Constant::getNullValue(Ty), Elem.first(),
10292 /*InsertBefore=*/nullptr,
10293 GlobalValue::NotThreadLocal, AddressSpaceVal);
10294 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10295 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10296 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10297 Elem.second = GV;
10298 }
10299
10300 return Elem.second;
10301}
10302
10303Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10304 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10305 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10306 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10307}
10308
10310 LLVMContext &Ctx = Builder.getContext();
10311 Value *Null =
10312 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10313 Value *SizeGep =
10314 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10315 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10316 return SizePtrToInt;
10317}
10318
10321 std::string VarName) {
10322 llvm::Constant *MaptypesArrayInit =
10323 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10324 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10325 M, MaptypesArrayInit->getType(),
10326 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10327 VarName);
10328 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10329 return MaptypesArrayGlobal;
10330}
10331
10333 InsertPointTy AllocaIP,
10334 unsigned NumOperands,
10335 struct MapperAllocas &MapperAllocas) {
10336 if (!updateToLocation(Loc))
10337 return;
10338
10339 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10340 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10341 Builder.restoreIP(AllocaIP);
10342 AllocaInst *ArgsBase = Builder.CreateAlloca(
10343 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10344 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10345 ".offload_ptrs");
10346 AllocaInst *ArgSizes = Builder.CreateAlloca(
10347 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10349 MapperAllocas.ArgsBase = ArgsBase;
10350 MapperAllocas.Args = Args;
10351 MapperAllocas.ArgSizes = ArgSizes;
10352}
10353
10355 Function *MapperFunc, Value *SrcLocInfo,
10356 Value *MaptypesArg, Value *MapnamesArg,
10358 int64_t DeviceID, unsigned NumOperands) {
10359 if (!updateToLocation(Loc))
10360 return;
10361
10362 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10363 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10364 Value *ArgsBaseGEP =
10365 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10366 {Builder.getInt32(0), Builder.getInt32(0)});
10367 Value *ArgsGEP =
10368 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10369 {Builder.getInt32(0), Builder.getInt32(0)});
10370 Value *ArgSizesGEP =
10371 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10372 {Builder.getInt32(0), Builder.getInt32(0)});
10373 Value *NullPtr =
10374 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10375 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10376 Builder.getInt32(NumOperands),
10377 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10378 MaptypesArg, MapnamesArg, NullPtr});
10379}
10380
10382 TargetDataRTArgs &RTArgs,
10383 TargetDataInfo &Info,
10384 bool ForEndCall) {
10385 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10386 "expected region end call to runtime only when end call is separate");
10387 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10388 auto VoidPtrTy = UnqualPtrTy;
10389 auto VoidPtrPtrTy = UnqualPtrTy;
10390 auto Int64Ty = Type::getInt64Ty(M.getContext());
10391 auto Int64PtrTy = UnqualPtrTy;
10392
10393 if (!Info.NumberOfPtrs) {
10394 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10395 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10396 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10397 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10398 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10399 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10400 return;
10401 }
10402
10403 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10404 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10405 Info.RTArgs.BasePointersArray,
10406 /*Idx0=*/0, /*Idx1=*/0);
10407 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10408 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10409 /*Idx0=*/0,
10410 /*Idx1=*/0);
10411 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10412 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10413 /*Idx0=*/0, /*Idx1=*/0);
10414 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10415 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10416 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10417 : Info.RTArgs.MapTypesArray,
10418 /*Idx0=*/0,
10419 /*Idx1=*/0);
10420
10421 // Only emit the mapper information arrays if debug information is
10422 // requested.
10423 if (!Info.EmitDebug)
10424 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10425 else
10426 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10427 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10428 /*Idx0=*/0,
10429 /*Idx1=*/0);
10430 // If there is no user-defined mapper, set the mapper array to nullptr to
10431 // avoid an unnecessary data privatization
10432 if (!Info.HasMapper)
10433 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10434 else
10435 RTArgs.MappersArray =
10436 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10437}
10438
10440 InsertPointTy CodeGenIP,
10441 MapInfosTy &CombinedInfo,
10442 TargetDataInfo &Info) {
10444 CombinedInfo.NonContigInfo;
10445
10446 // Build an array of struct descriptor_dim and then assign it to
10447 // offload_args.
10448 //
10449 // struct descriptor_dim {
10450 // uint64_t offset;
10451 // uint64_t count;
10452 // uint64_t stride
10453 // };
10454 Type *Int64Ty = Builder.getInt64Ty();
10456 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10457 "struct.descriptor_dim");
10458
10459 enum { OffsetFD = 0, CountFD, StrideFD };
10460 // We need two index variable here since the size of "Dims" is the same as
10461 // the size of Components, however, the size of offset, count, and stride is
10462 // equal to the size of base declaration that is non-contiguous.
10463 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10464 // Skip emitting ir if dimension size is 1 since it cannot be
10465 // non-contiguous.
10466 if (NonContigInfo.Dims[I] == 1)
10467 continue;
10468 Builder.restoreIP(AllocaIP);
10469 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10470 AllocaInst *DimsAddr =
10471 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10472 Builder.restoreIP(CodeGenIP);
10473 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10474 unsigned RevIdx = EE - II - 1;
10475 Value *DimsLVal = Builder.CreateInBoundsGEP(
10476 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10477 // Offset
10478 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10479 Builder.CreateAlignedStore(
10480 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10481 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10482 // Count
10483 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10484 Builder.CreateAlignedStore(
10485 NonContigInfo.Counts[L][RevIdx], CountLVal,
10486 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10487 // Stride
10488 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10489 Builder.CreateAlignedStore(
10490 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10491 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10492 }
10493 // args[I] = &dims
10494 Builder.restoreIP(CodeGenIP);
10495 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10496 DimsAddr, Builder.getPtrTy());
10497 Value *P = Builder.CreateConstInBoundsGEP2_32(
10498 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10499 Info.RTArgs.PointersArray, 0, I);
10500 Builder.CreateAlignedStore(
10501 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10502 ++L;
10503 }
10504}
10505
10506void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10507 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10508 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10509 BasicBlock *ExitBB, bool IsInit) {
10510 StringRef Prefix = IsInit ? ".init" : ".del";
10511
10512 // Evaluate if this is an array section.
10514 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10515 Value *IsArray =
10516 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10517 Value *DeleteBit = Builder.CreateAnd(
10518 MapType,
10519 Builder.getInt64(
10520 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10521 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10522 Value *DeleteCond;
10523 Value *Cond;
10524 if (IsInit) {
10525 // base != begin?
10526 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10527 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10528 DeleteCond = Builder.CreateIsNull(
10529 DeleteBit,
10530 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10531 } else {
10532 Cond = IsArray;
10533 DeleteCond = Builder.CreateIsNotNull(
10534 DeleteBit,
10535 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10536 }
10537 Cond = Builder.CreateAnd(Cond, DeleteCond);
10538 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10539
10540 emitBlock(BodyBB, MapperFn);
10541 // Get the array size by multiplying element size and element number (i.e., \p
10542 // Size).
10543 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10544 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10545 // memory allocation/deletion purpose only.
10546 Value *MapTypeArg = Builder.CreateAnd(
10547 MapType,
10548 Builder.getInt64(
10549 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10550 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10551 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10552 MapTypeArg = Builder.CreateOr(
10553 MapTypeArg,
10554 Builder.getInt64(
10555 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10556 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10557
10558 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10559 // data structure.
10560 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10561 ArraySize, MapTypeArg, MapName};
10563 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10564 OffloadingArgs);
10565}
10566
10569 llvm::Value *BeginArg)>
10570 GenMapInfoCB,
10571 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10572 bool PreserveMemberOfFlags, bool PropagatePresentToPointee) {
10573 SmallVector<Type *> Params;
10574 Params.emplace_back(Builder.getPtrTy());
10575 Params.emplace_back(Builder.getPtrTy());
10576 Params.emplace_back(Builder.getPtrTy());
10577 Params.emplace_back(Builder.getInt64Ty());
10578 Params.emplace_back(Builder.getInt64Ty());
10579 Params.emplace_back(Builder.getPtrTy());
10580
10581 auto *FnTy =
10582 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10583
10584 SmallString<64> TyStr;
10585 raw_svector_ostream Out(TyStr);
10586 Function *MapperFn =
10588 MapperFn->addFnAttr(Attribute::NoInline);
10589 MapperFn->addFnAttr(Attribute::NoUnwind);
10590 MapperFn->addParamAttr(0, Attribute::NoUndef);
10591 MapperFn->addParamAttr(1, Attribute::NoUndef);
10592 MapperFn->addParamAttr(2, Attribute::NoUndef);
10593 MapperFn->addParamAttr(3, Attribute::NoUndef);
10594 MapperFn->addParamAttr(4, Attribute::NoUndef);
10595 MapperFn->addParamAttr(5, Attribute::NoUndef);
10596
10597 // Start the mapper function code generation.
10598 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10599 auto SavedIP = Builder.saveIP();
10600 Builder.SetInsertPoint(EntryBB);
10601
10602 Value *MapperHandle = MapperFn->getArg(0);
10603 Value *BaseIn = MapperFn->getArg(1);
10604 Value *BeginIn = MapperFn->getArg(2);
10605 Value *Size = MapperFn->getArg(3);
10606 Value *MapType = MapperFn->getArg(4);
10607 Value *MapName = MapperFn->getArg(5);
10608
10609 // Compute the starting and end addresses of array elements.
10610 // Prepare common arguments for array initiation and deletion.
10611 // Convert the size in bytes into the number of array elements.
10612 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10613 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10614 Value *PtrBegin = BeginIn;
10615 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10616
10617 // Emit array initiation if this is an array section and \p MapType indicates
10618 // that memory allocation is required.
10619 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10620 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10621 MapType, MapName, ElementSize, HeadBB,
10622 /*IsInit=*/true);
10623
10624 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10625
10626 // Emit the loop header block.
10627 emitBlock(HeadBB, MapperFn);
10628 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10629 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10630 // Evaluate whether the initial condition is satisfied.
10631 Value *IsEmpty =
10632 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10633 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10634
10635 // Emit the loop body block.
10636 emitBlock(BodyBB, MapperFn);
10637 BasicBlock *LastBB = BodyBB;
10638 PHINode *PtrPHI =
10639 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10640 PtrPHI->addIncoming(PtrBegin, HeadBB);
10641
10642 // Get map clause information. Fill up the arrays with all mapped variables.
10643 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10644 if (!Info)
10645 return Info.takeError();
10646
10647 // Call the runtime API __tgt_mapper_num_components to get the number of
10648 // pre-existing components.
10649 Value *OffloadingArgs[] = {MapperHandle};
10650 Value *PreviousSize = createRuntimeFunctionCall(
10651 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10652 OffloadingArgs);
10653 Value *ShiftedPreviousSize =
10654 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10655
10656 // Fill up the runtime mapper handle for all components.
10657 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10658 Value *CurBaseArg = Info->BasePointers[I];
10659 Value *CurBeginArg = Info->Pointers[I];
10660 Value *CurSizeArg = Info->Sizes[I];
10661 Value *CurNameArg = Info->Names.size()
10662 ? Info->Names[I]
10663 : Constant::getNullValue(Builder.getPtrTy());
10664
10665 Value *OriMapType = Builder.getInt64(
10666 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10667 Info->Types[I]));
10668 auto RawType =
10669 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10670 Info->Types[I]);
10671 constexpr uint64_t MemberOfMask =
10672 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10673 constexpr uint64_t AttachBit =
10674 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10675 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10676
10677 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10678 // current array element (N = __tgt_mapper_num_components() at loop body
10679 // start).
10680 //
10681 // Example 1:
10682 // struct S { int x; int *p; };
10683 //
10684 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10685 // use: S arr[2]; ... map(arr)
10686 // entries per element:
10687 //
10688 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10689 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10690 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10691 //
10692 // Example 2:
10693 // struct S1 { int x; int y; };
10694 // struct S2 { int z; S1 *s1p; };
10695 //
10696 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10697 // s2.s1p->y)
10698 // use: S2 arr[2]; ... map(arr)
10699 // entries per element:
10700 //
10701 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10702 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10703 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10704 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10705 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10706 //
10707 // x/y carry inner MEMBER_OF(2)
10708 // which is shifted by N to become MEMBER_OF(N+2).
10709 //
10710 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10711 // the combined ALLOC entry for the s1p->x..y block, and the individual
10712 // x/y entries that are MEMBER_OF that block, all describe storage
10713 // reached through the attach ptr arr[i].s1p.
10714 //
10715 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10716 // linking them to the parent struct:
10717 //
10718 // * (*) Entries with HasAttachPtr: they represent pointee data that
10719 // occupies a different storage block than the struct being mapped, so
10720 // they are not a member of it. They may still be MEMBER_OF an entry
10721 // within that pointee block, in which case those pre-existing bits are
10722 // shifted -- see (***).
10723 // * (**) ATTACH entries: they are not a member of anything — they just
10724 // link a ptr to its ptee.
10725 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10726 // its pre-shaped entries already carry their final MEMBER_OF bits.
10727 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10728 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10729 // it.
10730 //
10731 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10732 // s1p->x/y entries above), those bits are still shifted by N.
10733 Value *MemberMapType;
10734 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10735 Info->HasAttachPtr[I]) {
10736 if (RawType & MemberOfMask)
10737 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10738 else
10739 MemberMapType = OriMapType;
10740 } else {
10741 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10742 }
10743
10744 // Combine the map type inherited from user-defined mapper with that
10745 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10746 // bits of the \a MapType, which is the input argument of the mapper
10747 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10748 // bits of MemberMapType.
10749 // [OpenMP 5.0], 1.2.6. map-type decay.
10750 // | alloc | to | from | tofrom | release | delete
10751 // ----------------------------------------------------------
10752 // alloc | alloc | alloc | alloc | alloc | release | delete
10753 // to | alloc | to | alloc | to | release | delete
10754 // from | alloc | alloc | from | from | release | delete
10755 // tofrom | alloc | to | from | tofrom | release | delete
10756 Value *LeftToFrom = Builder.CreateAnd(
10757 MapType,
10758 Builder.getInt64(
10759 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10760 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10761 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10762 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10763 BasicBlock *AllocElseBB =
10764 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10765 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10766 BasicBlock *ToElseBB =
10767 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10768 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10769 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10770 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10771 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10772 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10773 emitBlock(AllocBB, MapperFn);
10774 Value *AllocMapType = Builder.CreateAnd(
10775 MemberMapType,
10776 Builder.getInt64(
10777 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10778 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10779 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10780 Builder.CreateBr(EndBB);
10781 emitBlock(AllocElseBB, MapperFn);
10782 Value *IsTo = Builder.CreateICmpEQ(
10783 LeftToFrom,
10784 Builder.getInt64(
10785 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10786 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10787 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10788 // In case of to, clear OMP_MAP_FROM.
10789 emitBlock(ToBB, MapperFn);
10790 Value *ToMapType = Builder.CreateAnd(
10791 MemberMapType,
10792 Builder.getInt64(
10793 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10794 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10795 Builder.CreateBr(EndBB);
10796 emitBlock(ToElseBB, MapperFn);
10797 Value *IsFrom = Builder.CreateICmpEQ(
10798 LeftToFrom,
10799 Builder.getInt64(
10800 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10801 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10802 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10803 // In case of from, clear OMP_MAP_TO.
10804 emitBlock(FromBB, MapperFn);
10805 Value *FromMapType = Builder.CreateAnd(
10806 MemberMapType,
10807 Builder.getInt64(
10808 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10809 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10810 // In case of tofrom, do nothing.
10811 emitBlock(EndBB, MapperFn);
10812 LastBB = EndBB;
10813 PHINode *CurMapType =
10814 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10815 CurMapType->addIncoming(AllocMapType, AllocBB);
10816 CurMapType->addIncoming(ToMapType, ToBB);
10817 CurMapType->addIncoming(FromMapType, FromBB);
10818 CurMapType->addIncoming(MemberMapType, ToElseBB);
10819
10820 // Propagate map-type-modifying bits from the outer map clause to each map
10821 // inserted by the mapper.
10822 //
10823 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10824 // list item from the map clause and to apply the clauses specified in the
10825 // declared mapper to the construct on which the map clause appears...
10826 // If any modifier with the map-type-modifying property appears in the map
10827 // clause then the effect is as if that modifier appears in each map clause
10828 // specified in the declared mapper.
10829 //
10830 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10831 //
10832 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
10833 //
10834 // PRESENT is propagated only to entries that have an attach ptr
10835 // (HasAttachPtr): the pointee data, which occupies a different storage
10836 // block than the struct being mapped and so is not covered by the
10837 // present-check on the struct's own storage. A present modifier on the
10838 // outer clause must still require that pointee to be present on the device.
10839 //
10840 // This is gated on \p PropagatePresentToPointee (set by callers only for
10841 // OpenMP >= 6.0). Before 6.0 the present modifier is treated as not
10842 // applying to the pointee: the spec committee confirmed the divergence
10843 // between the present "motion" modifier (to/from) and the present map-type
10844 // modifier (map) was unintentional, to be fixed as an OpenMP 6.0 erratum,
10845 // so for 5.2 present is ignored for the pointee for both map and to/from.
10846 //
10847 // TODO: PRESENT should also be propagated to the struct's own members
10848 // (e.g. the s.x, s.y of map(present, mapper(id): s)) so that an absent
10849 // member triggers the present-check. We cannot do that yet: while pointer
10850 // members are mapped with PTR_AND_OBJ, a single combined entry allocates
10851 // the whole struct (including the pointer's storage), so propagating
10852 // PRESENT to it would wrongly require the pointer's pointee to be present.
10853 // Enable member propagation once Clang stops emitting PTR_AND_OBJ and uses
10854 // attach-style maps throughout.
10855 uint64_t ModifierBits =
10856 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10857 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
10858 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
10859 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
10860 if (PropagatePresentToPointee && Info->HasAttachPtr[I])
10861 ModifierBits |=
10862 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10863 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10864 Value *ImportedModifierBits =
10865 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
10866 Value *CurMapTypeWithModifiers = Builder.CreateOr(
10867 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
10868
10869 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
10870 // reserved for the attach(always) map-type modifier, and other modifier
10871 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
10872 Value *FinalMapType =
10873 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
10874
10875 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
10876 CurSizeArg, FinalMapType, CurNameArg};
10877
10878 auto ChildMapperFn = CustomMapperCB(I);
10879 if (!ChildMapperFn)
10880 return ChildMapperFn.takeError();
10881 if (*ChildMapperFn) {
10882 // Call the corresponding mapper function.
10883 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
10884 ->setDoesNotThrow();
10885 } else {
10886 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10887 // data structure.
10889 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10890 OffloadingArgs);
10891 }
10892 }
10893
10894 // Update the pointer to point to the next element that needs to be mapped,
10895 // and check whether we have mapped all elements.
10896 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
10897 "omp.arraymap.next");
10898 PtrPHI->addIncoming(PtrNext, LastBB);
10899 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
10900 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
10901 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
10902
10903 emitBlock(ExitBB, MapperFn);
10904 // Emit array deletion if this is an array section and \p MapType indicates
10905 // that deletion is required.
10906 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10907 MapType, MapName, ElementSize, DoneBB,
10908 /*IsInit=*/false);
10909
10910 // Emit the function exit block.
10911 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
10912
10913 Builder.CreateRetVoid();
10914 Builder.restoreIP(SavedIP);
10915 return MapperFn;
10916}
10917
10919 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
10920 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
10921 bool IsNonContiguous,
10922 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10923
10924 // Reset the array information.
10925 Info.clearArrayInfo();
10926 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
10927
10928 if (Info.NumberOfPtrs == 0)
10929 return Error::success();
10930
10931 Builder.restoreIP(AllocaIP);
10932 // Detect if we have any capture size requiring runtime evaluation of the
10933 // size so that a constant array could be eventually used.
10934 ArrayType *PointerArrayType =
10935 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
10936
10937 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
10938 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
10939
10940 Info.RTArgs.PointersArray = Builder.CreateAlloca(
10941 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
10942 AllocaInst *MappersArray = Builder.CreateAlloca(
10943 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
10944 Info.RTArgs.MappersArray = MappersArray;
10945
10946 // If we don't have any VLA types or other types that require runtime
10947 // evaluation, we can use a constant array for the map sizes, otherwise we
10948 // need to fill up the arrays as we do for the pointers.
10949 Type *Int64Ty = Builder.getInt64Ty();
10950 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
10951 ConstantInt::get(Int64Ty, 0));
10952 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
10953 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
10954 bool IsNonContigEntry =
10955 IsNonContiguous &&
10956 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10957 CombinedInfo.Types[I] &
10958 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
10959 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
10960 // descriptor_dim records), not the byte size.
10961 if (IsNonContigEntry) {
10962 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
10963 "Index must be in-bounds for NON_CONTIG Dims array");
10964 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
10965 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
10966 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
10967 continue;
10968 }
10969 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
10970 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
10971 ConstSizes[I] = CI;
10972 continue;
10973 }
10974 }
10975 RuntimeSizes.set(I);
10976 }
10977
10978 if (RuntimeSizes.all()) {
10979 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
10980 Info.RTArgs.SizesArray = Builder.CreateAlloca(
10981 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
10982 restoreIPandDebugLoc(Builder, CodeGenIP);
10983 } else {
10984 auto *SizesArrayInit = ConstantArray::get(
10985 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
10986 std::string Name = createPlatformSpecificName({"offload_sizes"});
10987 auto *SizesArrayGbl =
10988 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
10989 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
10990 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
10991
10992 if (!RuntimeSizes.any()) {
10993 Info.RTArgs.SizesArray = SizesArrayGbl;
10994 } else {
10995 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
10996 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
10997 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
10998 AllocaInst *Buffer = Builder.CreateAlloca(
10999 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11000 Buffer->setAlignment(OffloadSizeAlign);
11001 restoreIPandDebugLoc(Builder, CodeGenIP);
11002 Builder.CreateMemCpy(
11003 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11004 SizesArrayGbl, OffloadSizeAlign,
11005 Builder.getIntN(
11006 IndexSize,
11007 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11008
11009 Info.RTArgs.SizesArray = Buffer;
11010 }
11011 restoreIPandDebugLoc(Builder, CodeGenIP);
11012 }
11013
11014 // The map types are always constant so we don't need to generate code to
11015 // fill arrays. Instead, we create an array constant.
11017 for (auto mapFlag : CombinedInfo.Types)
11018 Mapping.push_back(
11019 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11020 mapFlag));
11021 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11022 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11023 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11024
11025 // The information types are only built if provided.
11026 if (!CombinedInfo.Names.empty()) {
11027 auto *MapNamesArrayGbl = createOffloadMapnames(
11028 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11029 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11030 Info.EmitDebug = true;
11031 } else {
11032 Info.RTArgs.MapNamesArray =
11034 Info.EmitDebug = false;
11035 }
11036
11037 // If there's a present map type modifier, it must not be applied to the end
11038 // of a region, so generate a separate map type array in that case.
11039 if (Info.separateBeginEndCalls()) {
11040 bool EndMapTypesDiffer = false;
11041 for (uint64_t &Type : Mapping) {
11042 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11043 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11044 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11045 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11046 EndMapTypesDiffer = true;
11047 }
11048 }
11049 if (EndMapTypesDiffer) {
11050 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11051 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11052 }
11053 }
11054
11055 PointerType *PtrTy = Builder.getPtrTy();
11056 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11057 Value *BPVal = CombinedInfo.BasePointers[I];
11058 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11059 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11060 0, I);
11061 Builder.CreateAlignedStore(BPVal, BP,
11062 M.getDataLayout().getPrefTypeAlign(PtrTy));
11063
11064 if (Info.requiresDevicePointerInfo()) {
11065 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11066 CodeGenIP = Builder.saveIP();
11067 Builder.restoreIP(AllocaIP);
11068 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11069 restoreIPandDebugLoc(Builder, CodeGenIP);
11070 if (DeviceAddrCB)
11071 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11072 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11073 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11074 if (DeviceAddrCB)
11075 DeviceAddrCB(I, BP);
11076 }
11077 }
11078
11079 Value *PVal = CombinedInfo.Pointers[I];
11080 Value *P = Builder.CreateConstInBoundsGEP2_32(
11081 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11082 I);
11083 // TODO: Check alignment correct.
11084 Builder.CreateAlignedStore(PVal, P,
11085 M.getDataLayout().getPrefTypeAlign(PtrTy));
11086
11087 if (RuntimeSizes.test(I)) {
11088 Value *S = Builder.CreateConstInBoundsGEP2_32(
11089 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11090 /*Idx0=*/0,
11091 /*Idx1=*/I);
11092 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11093 Int64Ty,
11094 /*isSigned=*/true),
11095 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11096 }
11097 // Fill up the mapper array.
11098 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11099 Value *MFunc = ConstantPointerNull::get(PtrTy);
11100
11101 auto CustomMFunc = CustomMapperCB(I);
11102 if (!CustomMFunc)
11103 return CustomMFunc.takeError();
11104 if (*CustomMFunc)
11105 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11106
11107 Value *MAddr = Builder.CreateInBoundsGEP(
11108 PointerArrayType, MappersArray,
11109 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11110 Builder.CreateAlignedStore(
11111 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11112 }
11113
11114 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11115 Info.NumberOfPtrs == 0)
11116 return Error::success();
11117 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11118 return Error::success();
11119}
11120
11122 BasicBlock *CurBB = Builder.GetInsertBlock();
11123
11124 if (!CurBB || CurBB->hasTerminator()) {
11125 // If there is no insert point or the previous block is already
11126 // terminated, don't touch it.
11127 } else {
11128 // Otherwise, create a fall-through branch.
11129 Builder.CreateBr(Target);
11130 }
11131
11132 Builder.ClearInsertionPoint();
11133}
11134
11136 bool IsFinished) {
11137 BasicBlock *CurBB = Builder.GetInsertBlock();
11138
11139 // Fall out of the current block (if necessary).
11140 emitBranch(BB);
11141
11142 if (IsFinished && BB->use_empty()) {
11143 BB->eraseFromParent();
11144 return;
11145 }
11146
11147 // Place the block after the current block, if possible, or else at
11148 // the end of the function.
11149 if (CurBB && CurBB->getParent())
11150 CurFn->insert(std::next(CurBB->getIterator()), BB);
11151 else
11152 CurFn->insert(CurFn->end(), BB);
11153 Builder.SetInsertPoint(BB);
11154}
11155
11157 BodyGenCallbackTy ElseGen,
11158 InsertPointTy AllocaIP,
11159 ArrayRef<BasicBlock *> DeallocBlocks) {
11160 // If the condition constant folds and can be elided, try to avoid emitting
11161 // the condition and the dead arm of the if/else.
11162 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11163 auto CondConstant = CI->getSExtValue();
11164 if (CondConstant)
11165 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11166
11167 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11168 }
11169
11170 Function *CurFn = Builder.GetInsertBlock()->getParent();
11171
11172 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11173 // emit the conditional branch.
11174 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11175 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11176 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11177 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11178 // Emit the 'then' code.
11179 emitBlock(ThenBlock, CurFn);
11180 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11181 return Err;
11182 emitBranch(ContBlock);
11183 // Emit the 'else' code if present.
11184 // There is no need to emit line number for unconditional branch.
11185 emitBlock(ElseBlock, CurFn);
11186 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11187 return Err;
11188 // There is no need to emit line number for unconditional branch.
11189 emitBranch(ContBlock);
11190 // Emit the continuation block for code after the if.
11191 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11192 return Error::success();
11193}
11194
11195bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11196 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11199 "Unexpected Atomic Ordering.");
11200
11201 bool Flush = false;
11203
11204 switch (AK) {
11205 case Read:
11208 FlushAO = AtomicOrdering::Acquire;
11209 Flush = true;
11210 }
11211 break;
11212 case Write:
11213 case Compare:
11214 case Update:
11217 FlushAO = AtomicOrdering::Release;
11218 Flush = true;
11219 }
11220 break;
11221 case Capture:
11222 switch (AO) {
11224 FlushAO = AtomicOrdering::Acquire;
11225 Flush = true;
11226 break;
11228 FlushAO = AtomicOrdering::Release;
11229 Flush = true;
11230 break;
11234 Flush = true;
11235 break;
11236 default:
11237 // do nothing - leave silently.
11238 break;
11239 }
11240 }
11241
11242 if (Flush) {
11243 // Currently Flush RT call still doesn't take memory_ordering, so for when
11244 // that happens, this tries to do the resolution of which atomic ordering
11245 // to use with but issue the flush call
11246 // TODO: pass `FlushAO` after memory ordering support is added
11247 (void)FlushAO;
11248 emitFlush(Loc);
11249 }
11250
11251 // for AO == AtomicOrdering::Monotonic and all other case combinations
11252 // do nothing
11253 return Flush;
11254}
11255
11259 AtomicOrdering AO, InsertPointTy AllocaIP) {
11260 if (!updateToLocation(Loc))
11261 return Loc.IP;
11262
11263 assert(X.Var->getType()->isPointerTy() &&
11264 "OMP Atomic expects a pointer to target memory");
11265 Type *XElemTy = X.ElemTy;
11266 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11267 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11268 "OMP atomic read expected a scalar type");
11269
11270 Value *XRead = nullptr;
11271
11272 if (XElemTy->isIntegerTy()) {
11273 LoadInst *XLD =
11274 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11275 XLD->setAtomic(AO);
11276 XRead = cast<Value>(XLD);
11277 } else if (XElemTy->isStructTy()) {
11278 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11279 // target does not support `atomicrmw` of the size of the struct
11280 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11281 OldVal->setAtomic(AO);
11282 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11283 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11284 OpenMPIRBuilder::AtomicInfo atomicInfo(
11285 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11286 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11287 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11288 XRead = AtomicLoadRes.first;
11289 OldVal->eraseFromParent();
11290 } else {
11291 // We need to perform atomic op as integer
11292 IntegerType *IntCastTy =
11293 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11294 LoadInst *XLoad =
11295 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11296 XLoad->setAtomic(AO);
11297 if (XElemTy->isFloatingPointTy()) {
11298 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11299 } else {
11300 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11301 }
11302 }
11303 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11304 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11305 return Builder.saveIP();
11306}
11307
11310 AtomicOpValue &X, Value *Expr,
11311 AtomicOrdering AO, InsertPointTy AllocaIP) {
11312 if (!updateToLocation(Loc))
11313 return Loc.IP;
11314
11315 assert(X.Var->getType()->isPointerTy() &&
11316 "OMP Atomic expects a pointer to target memory");
11317 Type *XElemTy = X.ElemTy;
11318 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11319 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11320 "OMP atomic write expected a scalar type");
11321
11322 if (XElemTy->isIntegerTy()) {
11323 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11324 XSt->setAtomic(AO);
11325 } else if (XElemTy->isStructTy()) {
11326 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11327 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11328 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11329 OpenMPIRBuilder::AtomicInfo atomicInfo(
11330 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11331 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11332 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11333 OldVal->eraseFromParent();
11334 } else {
11335 // We need to bitcast and perform atomic op as integers
11336 IntegerType *IntCastTy =
11337 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11338 Value *ExprCast =
11339 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11340 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11341 XSt->setAtomic(AO);
11342 }
11343
11344 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11345 return Builder.saveIP();
11346}
11347
11350 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11351 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11352 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11353 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11354 if (!updateToLocation(Loc))
11355 return Loc.IP;
11356
11357 LLVM_DEBUG({
11358 Type *XTy = X.Var->getType();
11359 assert(XTy->isPointerTy() &&
11360 "OMP Atomic expects a pointer to target memory");
11361 Type *XElemTy = X.ElemTy;
11362 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11363 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11364 "OMP atomic update expected a scalar or struct type");
11365 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11366 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&
11367 "OpenMP atomic does not support LT or GT operations");
11368 });
11369
11370 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11371 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11372 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11373 if (!AtomicResult)
11374 return AtomicResult.takeError();
11375 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11376 return Builder.saveIP();
11377}
11378
11379// FIXME: Duplicating AtomicExpand
11380Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11381 AtomicRMWInst::BinOp RMWOp) {
11382 switch (RMWOp) {
11383 case AtomicRMWInst::Add:
11384 return Builder.CreateAdd(Src1, Src2);
11385 case AtomicRMWInst::Sub:
11386 return Builder.CreateSub(Src1, Src2);
11387 case AtomicRMWInst::And:
11388 return Builder.CreateAnd(Src1, Src2);
11390 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11391 case AtomicRMWInst::Or:
11392 return Builder.CreateOr(Src1, Src2);
11393 case AtomicRMWInst::Xor:
11394 return Builder.CreateXor(Src1, Src2);
11399 case AtomicRMWInst::Max:
11400 case AtomicRMWInst::Min:
11413 llvm_unreachable("Unsupported atomic update operation");
11414 }
11415 llvm_unreachable("Unsupported atomic update operation");
11416}
11417
11419 // Loads cannot use Release or AcquireRelease ordering. This load is
11420 // just the initial value for the cmpxchg loop; the cmpxchg itself
11421 // retains the original ordering.
11422 AtomicOrdering LoadAO = AO;
11423
11424 if (AO == AtomicOrdering::Release) {
11426 } else if (AO == AtomicOrdering::AcquireRelease) {
11427 LoadAO = AtomicOrdering::Acquire;
11428 }
11429
11430 return LoadAO;
11431}
11432
11433Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11434 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11436 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11437 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11438 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11439 bool emitRMWOp = false;
11440 switch (RMWOp) {
11441 case AtomicRMWInst::Add:
11442 case AtomicRMWInst::And:
11444 case AtomicRMWInst::Or:
11445 case AtomicRMWInst::Xor:
11447 emitRMWOp = XElemTy;
11448 break;
11449 case AtomicRMWInst::Sub:
11450 emitRMWOp = (IsXBinopExpr && XElemTy);
11451 break;
11452 default:
11453 emitRMWOp = false;
11454 }
11455 emitRMWOp &= XElemTy->isIntegerTy();
11456
11457 std::pair<Value *, Value *> Res;
11458 if (emitRMWOp) {
11459 AtomicRMWInst *RMWInst =
11460 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11461 if (T.isAMDGPU()) {
11462 if (IsIgnoreDenormalMode)
11463 RMWInst->setMetadata("amdgpu.ignore.denormal.mode",
11464 llvm::MDNode::get(Builder.getContext(), {}));
11465 if (!IsFineGrainedMemory)
11466 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11467 llvm::MDNode::get(Builder.getContext(), {}));
11468 if (!IsRemoteMemory)
11469 RMWInst->setMetadata("amdgpu.no.remote.memory",
11470 llvm::MDNode::get(Builder.getContext(), {}));
11471 }
11472 Res.first = RMWInst;
11473 // not needed except in case of postfix captures. Generate anyway for
11474 // consistency with the else part. Will be removed with any DCE pass.
11475 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11476 if (RMWOp == AtomicRMWInst::Xchg)
11477 Res.second = Res.first;
11478 else
11479 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11480 } else if (XElemTy->isStructTy()) {
11481 LoadInst *OldVal =
11482 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11484 OldVal->setAtomic(LoadAO);
11485 const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
11486 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11487
11488 OpenMPIRBuilder::AtomicInfo atomicInfo(
11489 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11490 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11491 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11492 BasicBlock *CurBB = Builder.GetInsertBlock();
11493 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11494 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11495 BasicBlock *ExitBB =
11496 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11497 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11498 X->getName() + ".atomic.cont");
11499 ContBB->getTerminator()->eraseFromParent();
11500 Builder.restoreIP(AllocaIP);
11501 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11502 NewAtomicAddr->setName(X->getName() + "x.new.val");
11503 Builder.SetInsertPoint(ContBB);
11504 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11505 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11506 Value *OldExprVal = PHI;
11507 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11508 if (!CBResult)
11509 return CBResult.takeError();
11510 Value *Upd = *CBResult;
11511 Builder.CreateStore(Upd, NewAtomicAddr);
11514 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11515 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11516 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11517 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11518 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11519 OldVal->eraseFromParent();
11520 Res.first = OldExprVal;
11521 Res.second = Upd;
11522
11523 if (UnreachableInst *ExitTI =
11525 CurBBTI->eraseFromParent();
11526 Builder.SetInsertPoint(ExitBB);
11527 } else {
11528 Builder.SetInsertPoint(ExitTI);
11529 }
11530 } else {
11531 IntegerType *IntCastTy =
11532 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11533 LoadInst *OldVal =
11534 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11536 OldVal->setAtomic(LoadAO);
11537 // CurBB
11538 // | /---\
11539 // ContBB |
11540 // | \---/
11541 // ExitBB
11542 BasicBlock *CurBB = Builder.GetInsertBlock();
11543 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11544 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11545 BasicBlock *ExitBB =
11546 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11547 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11548 X->getName() + ".atomic.cont");
11549 ContBB->getTerminator()->eraseFromParent();
11550 Builder.restoreIP(AllocaIP);
11551 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11552 NewAtomicAddr->setName(X->getName() + "x.new.val");
11553 Builder.SetInsertPoint(ContBB);
11554 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11555 PHI->addIncoming(OldVal, CurBB);
11556 bool IsIntTy = XElemTy->isIntegerTy();
11557 Value *OldExprVal = PHI;
11558 if (!IsIntTy) {
11559 if (XElemTy->isFloatingPointTy()) {
11560 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11561 X->getName() + ".atomic.fltCast");
11562 } else {
11563 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11564 X->getName() + ".atomic.ptrCast");
11565 }
11566 }
11567
11568 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11569 if (!CBResult)
11570 return CBResult.takeError();
11571 Value *Upd = *CBResult;
11572 Builder.CreateStore(Upd, NewAtomicAddr);
11573 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11576 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11577 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11578 Result->setVolatile(VolatileX);
11579 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11580 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11581 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11582 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11583
11584 Res.first = OldExprVal;
11585 Res.second = Upd;
11586
11587 // set Insertion point in exit block
11588 if (UnreachableInst *ExitTI =
11590 CurBBTI->eraseFromParent();
11591 Builder.SetInsertPoint(ExitBB);
11592 } else {
11593 Builder.SetInsertPoint(ExitTI);
11594 }
11595 }
11596
11597 return Res;
11598}
11599
11602 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11603 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11604 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11605 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11606 if (!updateToLocation(Loc))
11607 return Loc.IP;
11608
11609 LLVM_DEBUG({
11610 Type *XTy = X.Var->getType();
11611 assert(XTy->isPointerTy() &&
11612 "OMP Atomic expects a pointer to target memory");
11613 Type *XElemTy = X.ElemTy;
11614 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11615 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11616 "OMP atomic capture expected a scalar or struct type");
11617 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11618 "OpenMP atomic does not support LT or GT operations");
11619 });
11620
11621 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11622 // 'x' is simply atomically rewritten with 'expr'.
11623 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11624 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11625 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11626 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11627 if (!AtomicResult)
11628 return AtomicResult.takeError();
11629 Value *CapturedVal =
11630 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11631 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11632
11633 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11634 return Builder.saveIP();
11635}
11636
11640 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11641 bool IsFailOnly, bool IsWeak) {
11642
11644 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11645 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11646}
11647
11651 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11652 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11653
11654 if (!updateToLocation(Loc))
11655 return Loc.IP;
11656
11657 assert(X.Var->getType()->isPointerTy() &&
11658 "OMP atomic expects a pointer to target memory");
11659 // compare capture
11660 if (V.Var) {
11661 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11662 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11663 }
11664
11665 bool IsInteger = E->getType()->isIntegerTy();
11666
11667 if (Op == OMPAtomicCompareOp::EQ) {
11668 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11669 // R.Var handling.
11670 Value *OldValue = nullptr;
11671 Value *SuccessOrFail = nullptr;
11672
11673 if (!IsInteger && HandleFPNegZero) {
11674 // IEEE 754 special cases for cmpxchg (which is bitwise):
11675 // 1. -0.0 == +0.0 but they have different bit patterns.
11676 // 2. NaN != NaN but identical NaN bit patterns would match.
11677 //
11678 // CurBB:
11679 // %e_int = bitcast E to intN
11680 // %d_int = bitcast D to intN
11681 // %x_curr = load atomic intN, X
11682 // %x_fp = bitcast %x_curr to FP
11683 // %e_is_nan = fcmp uno E, E
11684 // %x_is_nan = fcmp uno %x_fp, %x_fp
11685 // %either_nan = or %e_is_nan, %x_is_nan
11686 // br %either_nan, NaNBB, NotNaNBB
11687 // NaNBB: ; NaN == anything is always false
11688 // br ExitBB
11689 // NotNaNBB:
11690 // %x_is_zero = fcmp oeq %x_fp, 0.0
11691 // %e_is_zero = fcmp oeq E, 0.0
11692 // %both_zero = and %x_is_zero, %e_is_zero
11693 // br %both_zero, ZeroBB, NormalBB
11694 // ZeroBB: ; both ±0.0 → x = d
11695 // cmpxchg X, %x_curr, %d_int
11696 // br ExitBB
11697 // NormalBB: ; original path
11698 // cmpxchg X, %e_int, %d_int
11699 // br ExitBB
11700 // ExitBB:
11701 // phi merge
11702 IntegerType *IntCastTy =
11703 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11704 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11705 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11706
11707 // Load X atomically.
11708 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11709 X.Var->getName() + ".atomic.load");
11711 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11712
11713 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11714 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11715 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11716 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11717 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11718
11719 BasicBlock *CurBB = Builder.GetInsertBlock();
11720 Function *F = CurBB->getParent();
11721 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11722 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11723 BasicBlock *ExitBB =
11724 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11726 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11727 BasicBlock *NotNaNBB = BasicBlock::Create(
11728 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11730 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11731 BasicBlock *NormalBB = BasicBlock::Create(
11732 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11733
11734 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11735 CurBB->getTerminator()->eraseFromParent();
11736 Builder.SetInsertPoint(CurBB);
11737 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11738
11739 // NaNBB: NaN == anything is always false; skip cmpxchg.
11740 Builder.SetInsertPoint(NaNBB);
11741 Builder.CreateBr(ExitBB);
11742
11743 // NotNaNBB: check both X and E for ±0.0.
11744 Builder.SetInsertPoint(NotNaNBB);
11745 Value *XIsZero =
11746 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11747 X.Var->getName() + ".atomic.xiszero");
11748 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11749 "atomic.e.iszero");
11750 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11751 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11752
11753 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11754 Builder.SetInsertPoint(ZeroBB);
11755 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11756 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11757 ResZero->setWeak(IsWeak);
11758 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11759 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11760 Builder.CreateBr(ExitBB);
11761
11762 // NormalBB: original bitwise cmpxchg.
11763 Builder.SetInsertPoint(NormalBB);
11764 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11765 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11766 ResNormal->setWeak(IsWeak);
11767 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11768 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11769 Builder.CreateBr(ExitBB);
11770
11771 // ExitBB: merge results from NaN, Zero, and Normal paths.
11772 Builder.SetInsertPoint(ExitBB, ExitBB->begin());
11773 PHINode *OldIntPHI =
11774 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11775 OldIntPHI->addIncoming(XCurr, NaNBB);
11776 OldIntPHI->addIncoming(OldZero, ZeroBB);
11777 OldIntPHI->addIncoming(OldNormal, NormalBB);
11778 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11779 X.Var->getName() + ".atomic.ok");
11780 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11781 SuccessPHI->addIncoming(OkZero, ZeroBB);
11782 SuccessPHI->addIncoming(OkNormal, NormalBB);
11783
11784 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11785 CurBBTI->eraseFromParent();
11786 Builder.SetInsertPoint(ExitBB);
11787 } else {
11788 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11789 }
11790
11791 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11792 X.Var->getName() + ".atomic.old.fp");
11793 SuccessOrFail = SuccessPHI;
11794 } else {
11795 AtomicCmpXchgInst *Result = nullptr;
11796 if (!IsInteger) {
11797 IntegerType *IntCastTy =
11798 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11799 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11800 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11801 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11802 MaybeAlign(), AO, Failure);
11803 } else {
11804 Result =
11805 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11806 }
11807 Result->setWeak(IsWeak);
11808
11809 if (V.Var) {
11810 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11811 if (!IsInteger)
11812 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11813 assert(OldValue->getType() == V.ElemTy &&
11814 "OldValue and V must be of same type");
11815 if (IsPostfixUpdate) {
11816 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11817 } else {
11818 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11819 if (IsFailOnly) {
11820 BasicBlock *CurBB = Builder.GetInsertBlock();
11821 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11822 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11823 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11824 CurBBTI, X.Var->getName() + ".atomic.exit");
11825 BasicBlock *ContBB = CurBB->splitBasicBlock(
11826 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11827 ContBB->getTerminator()->eraseFromParent();
11828 CurBB->getTerminator()->eraseFromParent();
11829
11830 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11831
11832 Builder.SetInsertPoint(ContBB);
11833 Builder.CreateStore(OldValue, V.Var);
11834 Builder.CreateBr(ExitBB);
11835
11836 if (UnreachableInst *ExitTI =
11838 CurBBTI->eraseFromParent();
11839 Builder.SetInsertPoint(ExitBB);
11840 } else {
11841 Builder.SetInsertPoint(ExitTI);
11842 }
11843 } else {
11844 Value *CapturedValue =
11845 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11846 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11847 }
11848 }
11849 }
11850 // The comparison result has to be stored.
11851 if (R.Var) {
11852 assert(R.Var->getType()->isPointerTy() &&
11853 "r.var must be of pointer type");
11854 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11855
11856 Value *SuccessFailureVal =
11857 Builder.CreateExtractValue(Result, /*Idxs=*/1);
11858 Value *ResultCast =
11859 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
11860 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
11861 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11862 }
11863 }
11864
11865 // For the HandleFPNegZero path, handle V.Var and R.Var using the
11866 // pre-computed OldValue and SuccessOrFail.
11867 if (HandleFPNegZero && !IsInteger) {
11868 if (V.Var) {
11869 assert(OldValue->getType() == V.ElemTy &&
11870 "OldValue and V must be of same type");
11871 if (IsPostfixUpdate) {
11872 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11873 } else {
11874 if (IsFailOnly) {
11875 BasicBlock *CurBB = Builder.GetInsertBlock();
11876 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11877 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11878 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11879 CurBBTI, X.Var->getName() + ".atomic.exit");
11880 BasicBlock *ContBB = CurBB->splitBasicBlock(
11881 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11882 ContBB->getTerminator()->eraseFromParent();
11883 CurBB->getTerminator()->eraseFromParent();
11884
11885 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11886
11887 Builder.SetInsertPoint(ContBB);
11888 Builder.CreateStore(OldValue, V.Var);
11889 Builder.CreateBr(ExitBB);
11890
11891 if (UnreachableInst *ExitTI =
11893 CurBBTI->eraseFromParent();
11894 Builder.SetInsertPoint(ExitBB);
11895 } else {
11896 Builder.SetInsertPoint(ExitTI);
11897 }
11898 } else {
11899 Value *CapturedValue =
11900 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11901 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11902 }
11903 }
11904 }
11905 // The comparison result has to be stored.
11906 if (R.Var) {
11907 assert(R.Var->getType()->isPointerTy() &&
11908 "r.var must be of pointer type");
11909 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11910
11911 Value *ResultCast = R.IsSigned
11912 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
11913 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
11914 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11915 }
11916 }
11917 } else {
11918 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
11919 "Op should be either max or min at this point");
11920 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
11921
11922 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
11923 // Let's take max as example.
11924 // OpenMP form:
11925 // x = x > expr ? expr : x;
11926 // LLVM form:
11927 // *ptr = *ptr > val ? *ptr : val;
11928 // We need to transform to LLVM form.
11929 // x = x <= expr ? x : expr;
11931 if (IsXBinopExpr) {
11932 if (IsInteger) {
11933 if (X.IsSigned)
11934 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
11936 else
11937 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
11939 } else {
11940 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
11942 }
11943 } else {
11944 if (IsInteger) {
11945 if (X.IsSigned)
11946 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
11948 else
11949 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
11951 } else {
11952 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
11954 }
11955 }
11956
11957 AtomicRMWInst *OldValue =
11958 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
11959 if (V.Var) {
11960 Value *CapturedValue = nullptr;
11961 if (IsPostfixUpdate) {
11962 CapturedValue = OldValue;
11963 } else {
11964 CmpInst::Predicate Pred;
11965 switch (NewOp) {
11966 case AtomicRMWInst::Max:
11967 Pred = CmpInst::ICMP_SGT;
11968 break;
11970 Pred = CmpInst::ICMP_UGT;
11971 break;
11973 Pred = CmpInst::FCMP_OGT;
11974 break;
11975 case AtomicRMWInst::Min:
11976 Pred = CmpInst::ICMP_SLT;
11977 break;
11979 Pred = CmpInst::ICMP_ULT;
11980 break;
11982 Pred = CmpInst::FCMP_OLT;
11983 break;
11984 default:
11985 llvm_unreachable("unexpected comparison op");
11986 }
11987 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
11988 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
11989 }
11990 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11991 }
11992 }
11993
11994 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
11995
11996 return Builder.saveIP();
11997}
11998
12001 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12002 Value *NumTeamsUpper, Value *ThreadLimit,
12003 Value *IfExpr) {
12004 if (!updateToLocation(Loc))
12005 return InsertPointTy();
12006
12007 uint32_t SrcLocStrSize;
12008 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12009 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12010 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12011
12012 // Outer allocation basicblock is the entry block of the current function.
12013 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12014 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12015 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12016 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12017 }
12018
12019 // The current basic block is split into four basic blocks. After outlining,
12020 // they will be mapped as follows:
12021 // ```
12022 // def current_fn() {
12023 // current_basic_block:
12024 // br label %teams.exit
12025 // teams.exit:
12026 // ; instructions after teams
12027 // }
12028 //
12029 // def outlined_fn() {
12030 // teams.alloca:
12031 // br label %teams.body
12032 // teams.body:
12033 // ; instructions within teams body
12034 // }
12035 // ```
12036 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12037 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12038 BasicBlock *AllocaBB =
12039 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12040
12041 bool SubClausesPresent =
12042 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12043 // Push num_teams
12044 if (!Config.isTargetDevice() && SubClausesPresent) {
12045 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12046 "if lowerbound is non-null, then upperbound must also be non-null "
12047 "for bounds on num_teams");
12048
12049 if (NumTeamsUpper == nullptr)
12050 NumTeamsUpper = Builder.getInt32(0);
12051
12052 if (NumTeamsLower == nullptr)
12053 NumTeamsLower = NumTeamsUpper;
12054
12055 if (IfExpr) {
12056 assert(IfExpr->getType()->isIntegerTy() &&
12057 "argument to if clause must be an integer value");
12058
12059 // upper = ifexpr ? upper : 1
12060 if (IfExpr->getType() != Int1)
12061 IfExpr = Builder.CreateICmpNE(IfExpr,
12062 ConstantInt::get(IfExpr->getType(), 0));
12063 NumTeamsUpper = Builder.CreateSelect(
12064 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12065
12066 // lower = ifexpr ? lower : 1
12067 NumTeamsLower = Builder.CreateSelect(
12068 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12069 }
12070
12071 if (ThreadLimit == nullptr)
12072 ThreadLimit = Builder.getInt32(0);
12073
12074 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12075 // truncate or sign extend the passed values to match the int32 parameters.
12076 Value *NumTeamsLowerInt32 =
12077 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12078 Value *NumTeamsUpperInt32 =
12079 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12080 Value *ThreadLimitInt32 =
12081 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12082
12083 Value *ThreadNum = getOrCreateThreadID(Ident);
12084
12086 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12087 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12088 ThreadLimitInt32});
12089 }
12090 // Generate the body of teams.
12091 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12092 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12093 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12094 return Err;
12095
12096 auto OI = std::make_unique<OutlineInfo>();
12097 OI->EntryBB = AllocaBB;
12098 OI->ExitBB = ExitBB;
12099 OI->OuterAllocBB = &OuterAllocaBB;
12100
12101 // Insert fake values for global tid and bound tid.
12103 InsertPointTy OuterAllocaIP(&OuterAllocaBB, OuterAllocaBB.begin());
12104 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12105 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12106 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12107 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12108
12109 auto HostPostOutlineCB = [this, Ident,
12110 ToBeDeleted](Function &OutlinedFn) mutable {
12111 // The stale call instruction will be replaced with a new call instruction
12112 // for runtime call with the outlined function.
12113
12114 assert(OutlinedFn.hasOneUse() &&
12115 "there must be a single user for the outlined function");
12116 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12117 ToBeDeleted.push_back(StaleCI);
12118
12119 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12120 "Outlined function must have two or three arguments only");
12121
12122 bool HasShared = OutlinedFn.arg_size() == 3;
12123
12124 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12125 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12126 if (HasShared)
12127 OutlinedFn.getArg(2)->setName("data");
12128
12129 // Call to the runtime function for teams in the current function.
12130 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12131 "outlined function.");
12132 Builder.SetInsertPoint(StaleCI);
12133 SmallVector<Value *> Args = {
12134 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12135 if (HasShared)
12136 Args.push_back(StaleCI->getArgOperand(2));
12139 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12140 Args);
12141
12142 for (Instruction *I : llvm::reverse(ToBeDeleted))
12143 I->eraseFromParent();
12144 };
12145
12146 if (!Config.isTargetDevice())
12147 OI->PostOutlineCB = HostPostOutlineCB;
12148
12149 addOutlineInfo(std::move(OI));
12150
12151 Builder.SetInsertPoint(ExitBB);
12152
12153 return Builder.saveIP();
12154}
12155
12157 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12158 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12159 if (!updateToLocation(Loc))
12160 return InsertPointTy();
12161
12162 BasicBlock *OuterAllocaBB = OuterAllocIP.getBlock();
12163
12164 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12165 BasicBlock *BodyBB =
12166 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12167 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12168 }
12169 BasicBlock *ExitBB =
12170 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12171 BasicBlock *BodyBB =
12172 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12173 BasicBlock *AllocaBB =
12174 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12175
12176 // Generate the body of distribute clause
12177 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12178 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12179 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12180 return Err;
12181
12182 // When using target we use different runtime functions which require a
12183 // callback.
12184 if (Config.isTargetDevice()) {
12185 auto OI = std::make_unique<OutlineInfo>();
12186 OI->OuterAllocBB = OuterAllocIP.getBlock();
12187 OI->EntryBB = AllocaBB;
12188 OI->ExitBB = ExitBB;
12189 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12190 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12191
12192 addOutlineInfo(std::move(OI));
12193 }
12194 Builder.SetInsertPoint(ExitBB);
12195
12196 return Builder.saveIP();
12197}
12198
12201 std::string VarName) {
12202 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12204 Names.size()),
12205 Names);
12206 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12207 M, MapNamesArrayInit->getType(),
12208 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12209 VarName);
12210 return MapNamesArrayGlobal;
12211}
12212
12213// Create all simple and struct types exposed by the runtime and remember
12214// the llvm::PointerTypes of them for easy access later.
12215void OpenMPIRBuilder::initializeTypes(Module &M) {
12216 LLVMContext &Ctx = M.getContext();
12217 StructType *T;
12218 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12219 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12220#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12221#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12222 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12223 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12224#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12225 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12226 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12227#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12228 T = StructType::getTypeByName(Ctx, StructName); \
12229 if (!T) \
12230 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12231 VarName = T; \
12232 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12233#include "llvm/Frontend/OpenMP/OMPKinds.def"
12234}
12235
12238 SmallVectorImpl<BasicBlock *> &BlockVector) {
12240 BlockSet.insert(EntryBB);
12241 BlockSet.insert(ExitBB);
12242
12243 Worklist.push_back(EntryBB);
12244 while (!Worklist.empty()) {
12245 BasicBlock *BB = Worklist.pop_back_val();
12246 BlockVector.push_back(BB);
12247 for (BasicBlock *SuccBB : successors(BB))
12248 if (BlockSet.insert(SuccBB).second)
12249 Worklist.push_back(SuccBB);
12250 }
12251}
12252
12253std::unique_ptr<CodeExtractor>
12255 bool ArgsInZeroAddressSpace,
12256 Twine Suffix) {
12257 return std::make_unique<CodeExtractor>(
12258 Blocks, /* DominatorTree */ nullptr,
12259 /* AggregateArgs */ true,
12260 /* BlockFrequencyInfo */ nullptr,
12261 /* BranchProbabilityInfo */ nullptr,
12262 /* AssumptionCache */ nullptr,
12263 /* AllowVarArgs */ true,
12264 /* AllowAlloca */ true,
12265 /* AllocationBlock*/ OuterAllocBB,
12266 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12267 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12268}
12269
12270std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12271 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12272 return std::make_unique<DeviceSharedMemCodeExtractor>(
12273 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12274 /* AggregateArgs */ true,
12275 /* BlockFrequencyInfo */ nullptr,
12276 /* BranchProbabilityInfo */ nullptr,
12277 /* AssumptionCache */ nullptr,
12278 /* AllowVarArgs */ true,
12279 /* AllowAlloca */ true,
12280 /* AllocationBlock*/ OuterAllocBB,
12281 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12283 : OuterDeallocBBs,
12284 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12285}
12286
12288 uint64_t Size, int32_t Flags,
12290 StringRef Name) {
12291 if (!Config.isGPU()) {
12294 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12295 return;
12296 }
12297 // TODO: Add support for global variables on the device after declare target
12298 // support.
12299 Function *Fn = dyn_cast<Function>(Addr);
12300 if (!Fn)
12301 return;
12302
12303 // Add a function attribute for the kernel.
12304 Fn->addFnAttr("kernel");
12305 if (T.isAMDGCN())
12306 Fn->addFnAttr("uniform-work-group-size");
12307 Fn->addFnAttr(Attribute::MustProgress);
12308}
12309
12310// We only generate metadata for function that contain target regions.
12313
12314 // If there are no entries, we don't need to do anything.
12315 if (OffloadInfoManager.empty())
12316 return;
12317
12318 LLVMContext &C = M.getContext();
12321 16>
12322 OrderedEntries(OffloadInfoManager.size());
12323
12324 // Auxiliary methods to create metadata values and strings.
12325 auto &&GetMDInt = [this](unsigned V) {
12326 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12327 };
12328
12329 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12330
12331 // Create the offloading info metadata node.
12332 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12333 auto &&TargetRegionMetadataEmitter =
12334 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12335 const TargetRegionEntryInfo &EntryInfo,
12337 // Generate metadata for target regions. Each entry of this metadata
12338 // contains:
12339 // - Entry 0 -> Kind of this type of metadata (0).
12340 // - Entry 1 -> Device ID of the file where the entry was identified.
12341 // - Entry 2 -> File ID of the file where the entry was identified.
12342 // - Entry 3 -> Mangled name of the function where the entry was
12343 // identified.
12344 // - Entry 4 -> Line in the file where the entry was identified.
12345 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12346 // - Entry 6 -> Order the entry was created.
12347 // The first element of the metadata node is the kind.
12348 Metadata *Ops[] = {
12349 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12350 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12351 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12352 GetMDInt(E.getOrder())};
12353
12354 // Save this entry in the right position of the ordered entries array.
12355 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12356
12357 // Add metadata to the named metadata node.
12358 MD->addOperand(MDNode::get(C, Ops));
12359 };
12360
12361 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12362
12363 // Create function that emits metadata for each device global variable entry;
12364 auto &&DeviceGlobalVarMetadataEmitter =
12365 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12366 StringRef MangledName,
12368 // Generate metadata for global variables. Each entry of this metadata
12369 // contains:
12370 // - Entry 0 -> Kind of this type of metadata (1).
12371 // - Entry 1 -> Mangled name of the variable.
12372 // - Entry 2 -> Declare target kind.
12373 // - Entry 3 -> Order the entry was created.
12374 // The first element of the metadata node is the kind.
12375 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12376 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12377
12378 // Save this entry in the right position of the ordered entries array.
12379 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12380 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12381
12382 // Add metadata to the named metadata node.
12383 MD->addOperand(MDNode::get(C, Ops));
12384 };
12385
12386 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12387 DeviceGlobalVarMetadataEmitter);
12388
12389 for (const auto &E : OrderedEntries) {
12390 assert(E.first && "All ordered entries must exist!");
12391 if (const auto *CE =
12393 E.first)) {
12394 if (!CE->getID() || !CE->getAddress()) {
12395 // Do not blame the entry if the parent funtion is not emitted.
12396 TargetRegionEntryInfo EntryInfo = E.second;
12397 StringRef FnName = EntryInfo.ParentName;
12398 if (!M.getNamedValue(FnName))
12399 continue;
12400 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12401 continue;
12402 }
12403 createOffloadEntry(CE->getID(), CE->getAddress(),
12404 /*Size=*/0, CE->getFlags(),
12406 } else if (const auto *CE = dyn_cast<
12408 E.first)) {
12411 CE->getFlags());
12412 switch (Flags) {
12415 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12416 continue;
12417 if (!CE->getAddress()) {
12418 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12419 continue;
12420 }
12421 // The vaiable has no definition - no need to add the entry.
12422 if (CE->getVarSize() == 0)
12423 continue;
12424 break;
12426 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12427 (!Config.isTargetDevice() && CE->getAddress())) &&
12428 "Declaret target link address is set.");
12429 if (Config.isTargetDevice())
12430 continue;
12431 if (!CE->getAddress()) {
12433 continue;
12434 }
12435 break;
12438 if (!CE->getAddress()) {
12439 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12440 continue;
12441 }
12442 break;
12443 default:
12444 break;
12445 }
12446
12447 // Hidden or internal symbols on the device are not externally visible.
12448 // We should not attempt to register them by creating an offloading
12449 // entry. Indirect variables are handled separately on the device.
12450 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12451 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12452 (Flags !=
12454 Flags != OffloadEntriesInfoManager::
12455 OMPTargetGlobalVarEntryIndirectVTable))
12456 continue;
12457
12458 // Indirect globals need to use a special name that doesn't match the name
12459 // of the associated host global.
12461 Flags ==
12463 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12464 Flags, CE->getLinkage(), CE->getVarName());
12465 else
12466 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12467 Flags, CE->getLinkage());
12468
12469 } else {
12470 llvm_unreachable("Unsupported entry kind.");
12471 }
12472 }
12473
12474 // Emit requires directive globals to a special entry so the runtime can
12475 // register them when the device image is loaded.
12476 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12477 // entries should be redesigned to better suit this use-case.
12478 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12482 ".requires", /*Size=*/0,
12484 Config.getRequiresFlags());
12485}
12486
12489 unsigned FileID, unsigned Line, unsigned Count) {
12490 raw_svector_ostream OS(Name);
12491 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12492 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12493 if (Count)
12494 OS << "_" << Count;
12495}
12496
12498 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12499 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12501 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12502 EntryInfo.Line, NewCount);
12503}
12504
12507 vfs::FileSystem &VFS,
12508 StringRef ParentName) {
12509 sys::fs::UniqueID ID(0xdeadf17e, 0);
12510 auto FileIDInfo = CallBack();
12511 uint64_t FileID = 0;
12512 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12513 ID = Status->getUniqueID();
12514 FileID = Status->getUniqueID().getFile();
12515 } else {
12516 // If the inode ID could not be determined, create a hash value
12517 // the current file name and use that as an ID.
12518 FileID = hash_value(std::get<0>(FileIDInfo));
12519 }
12520
12521 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12522 std::get<1>(FileIDInfo));
12523}
12524
12526 unsigned Offset = 0;
12527 for (uint64_t Remain =
12528 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12530 !(Remain & 1); Remain = Remain >> 1)
12531 Offset++;
12532 return Offset;
12533}
12534
12537 // Rotate by getFlagMemberOffset() bits.
12538 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12539 << getFlagMemberOffset());
12540}
12541
12544 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12545 // If the entry is PTR_AND_OBJ but has not been marked with the special
12546 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12547 // marked as MEMBER_OF.
12548 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12550 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12553 return;
12554
12555 // Entries with ATTACH are not members-of anything. They are handled
12556 // separately by the runtime after other maps have been handled.
12557 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12559 return;
12560
12561 // Reset the placeholder value to prepare the flag for the assignment of the
12562 // proper MEMBER_OF value.
12563 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12564 Flags |= MemberOfFlag;
12565}
12566
12570 bool IsDeclaration, bool IsExternallyVisible,
12571 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12572 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12573 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12574 std::function<Constant *()> GlobalInitializer,
12575 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12576 // TODO: convert this to utilise the IRBuilder Config rather than
12577 // a passed down argument.
12578 if (OpenMPSIMD)
12579 return nullptr;
12580
12583 CaptureClause ==
12585 Config.hasRequiresUnifiedSharedMemory())) {
12586 SmallString<64> PtrName;
12587 {
12588 raw_svector_ostream OS(PtrName);
12589 OS << MangledName;
12590 if (!IsExternallyVisible)
12591 OS << format("_%x", EntryInfo.FileID);
12592 OS << "_decl_tgt_ref_ptr";
12593 }
12594
12595 Value *Ptr = M.getNamedValue(PtrName);
12596
12597 if (!Ptr) {
12598 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12599 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12600
12601 auto *GV = cast<GlobalVariable>(Ptr);
12602 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12603
12604 if (!Config.isTargetDevice()) {
12605 if (GlobalInitializer)
12606 GV->setInitializer(GlobalInitializer());
12607 else
12608 GV->setInitializer(GlobalValue);
12609 }
12610
12612 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12613 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12614 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12615 }
12616
12617 return cast<Constant>(Ptr);
12618 }
12619
12620 return nullptr;
12621}
12622
12626 bool IsDeclaration, bool IsExternallyVisible,
12627 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12628 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12629 std::vector<Triple> TargetTriple,
12630 std::function<Constant *()> GlobalInitializer,
12631 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12632 Constant *Addr) {
12634 (TargetTriple.empty() && !Config.isTargetDevice()))
12635 return;
12636
12638 StringRef VarName;
12639 int64_t VarSize;
12641
12643 CaptureClause ==
12645 !Config.hasRequiresUnifiedSharedMemory()) {
12647 VarName = MangledName;
12648 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12649
12650 if (!IsDeclaration)
12651 VarSize = divideCeil(
12652 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12653 else
12654 VarSize = 0;
12655 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12656
12657 // This is a workaround carried over from Clang which prevents undesired
12658 // optimisation of internal variables.
12659 if (Config.isTargetDevice() &&
12660 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12661 // Do not create a "ref-variable" if the original is not also available
12662 // on the host.
12663 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12664 return;
12665
12666 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12667
12668 if (!M.getNamedValue(RefName)) {
12669 Constant *AddrRef =
12670 getOrCreateInternalVariable(Addr->getType(), RefName);
12671 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12672 GvAddrRef->setConstant(true);
12673 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12674 GvAddrRef->setInitializer(Addr);
12675 GeneratedRefs.push_back(GvAddrRef);
12676 }
12677 }
12678 } else {
12681 else
12683
12684 if (Config.isTargetDevice()) {
12685 VarName = (Addr) ? Addr->getName() : "";
12686 Addr = nullptr;
12687 } else {
12689 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12690 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12691 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12692 VarName = (Addr) ? Addr->getName() : "";
12693 }
12694 VarSize = M.getDataLayout().getPointerSize();
12696 }
12697
12698 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12699 Flags, Linkage);
12700}
12701
12702/// Loads all the offload entries information from the host IR
12703/// metadata.
12705 // If we are in target mode, load the metadata from the host IR. This code has
12706 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12707
12708 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12709 if (!MD)
12710 return;
12711
12712 for (MDNode *MN : MD->operands()) {
12713 auto &&GetMDInt = [MN](unsigned Idx) {
12714 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12715 return cast<ConstantInt>(V->getValue())->getZExtValue();
12716 };
12717
12718 auto &&GetMDString = [MN](unsigned Idx) {
12719 auto *V = cast<MDString>(MN->getOperand(Idx));
12720 return V->getString();
12721 };
12722
12723 switch (GetMDInt(0)) {
12724 default:
12725 llvm_unreachable("Unexpected metadata!");
12726 break;
12727 case OffloadEntriesInfoManager::OffloadEntryInfo::
12728 OffloadingEntryInfoTargetRegion: {
12729 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12730 /*DeviceID=*/GetMDInt(1),
12731 /*FileID=*/GetMDInt(2),
12732 /*Line=*/GetMDInt(4),
12733 /*Count=*/GetMDInt(5));
12734 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12735 /*Order=*/GetMDInt(6));
12736 break;
12737 }
12738 case OffloadEntriesInfoManager::OffloadEntryInfo::
12739 OffloadingEntryInfoDeviceGlobalVar:
12740 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12741 /*MangledName=*/GetMDString(1),
12743 /*Flags=*/GetMDInt(2)),
12744 /*Order=*/GetMDInt(3));
12745 break;
12746 }
12747 }
12748}
12749
12751 StringRef HostFilePath) {
12752 if (HostFilePath.empty())
12753 return;
12754
12755 auto Buf = VFS.getBufferForFile(HostFilePath);
12756 if (std::error_code Err = Buf.getError()) {
12757 report_fatal_error(("error opening host file from host file path inside of "
12758 "OpenMPIRBuilder: " +
12759 Err.message())
12760 .c_str());
12761 }
12762
12763 LLVMContext Ctx;
12765 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12766 if (std::error_code Err = M.getError()) {
12768 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12769 .c_str());
12770 }
12771
12772 loadOffloadInfoMetadata(*M.get());
12773}
12774
12777 llvm::StringRef Name) {
12778 Builder.restoreIP(Loc.IP);
12779
12780 BasicBlock *CurBB = Builder.GetInsertBlock();
12781 assert(CurBB &&
12782 "expected a valid insertion block for creating an iterator loop");
12783 Function *F = CurBB->getParent();
12784
12785 InsertPointTy SplitIP = Builder.saveIP();
12786 if (SplitIP.getPoint() == CurBB->end())
12787 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12788 SplitIP = InsertPointTy(CurBB, Terminator->getIterator());
12789
12790 BasicBlock *ContBB =
12791 splitBB(SplitIP, /*CreateBranch=*/false,
12792 Builder.getCurrentDebugLocation(), "omp.it.cont");
12793
12794 CanonicalLoopInfo *CLI =
12795 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12796 /*PreInsertBefore=*/ContBB,
12797 /*PostInsertBefore=*/ContBB, Name);
12798
12799 // Enter loop from original block.
12800 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12801
12802 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12803 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12804 T->eraseFromParent();
12805
12806 InsertPointTy BodyIP = CLI->getBodyIP();
12807 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12808 return Err;
12809
12810 // Body must either fallthrough to the latch or branch directly to it.
12811 if (Instruction *BodyTerminator = CLI->getBody()->getTerminatorOrNull()) {
12812 auto *BodyBr = dyn_cast<UncondBrInst>(BodyTerminator);
12813 if (!BodyBr || BodyBr->getSuccessor() != CLI->getLatch()) {
12815 "iterator bodygen must terminate the canonical body with an "
12816 "unconditional branch to the loop latch",
12818 }
12819 } else {
12820 // Ensure we end the loop body by jumping to the latch.
12821 Builder.SetInsertPoint(CLI->getBody());
12822 Builder.CreateBr(CLI->getLatch());
12823 }
12824
12825 // Link After -> ContBB
12826 Builder.SetInsertPoint(CLI->getAfter(), CLI->getAfter()->begin());
12827 if (!CLI->getAfter()->hasTerminator())
12828 Builder.CreateBr(ContBB);
12829
12830 return InsertPointTy{ContBB, ContBB->begin()};
12831}
12832
12833/// Mangle the parameter part of the vector function name according to
12834/// their OpenMP classification. The mangling function is defined in
12835/// section 4.5 of the AAVFABI(2021Q1).
12836static std::string mangleVectorParameters(
12838 SmallString<256> Buffer;
12839 llvm::raw_svector_ostream Out(Buffer);
12840 for (const auto &ParamAttr : ParamAttrs) {
12841 switch (ParamAttr.Kind) {
12843 Out << 'l';
12844 break;
12846 Out << 'R';
12847 break;
12849 Out << 'U';
12850 break;
12852 Out << 'L';
12853 break;
12855 Out << 'u';
12856 break;
12858 Out << 'v';
12859 break;
12860 }
12861 if (ParamAttr.HasVarStride)
12862 Out << "s" << ParamAttr.StrideOrArg;
12863 else if (ParamAttr.Kind ==
12865 ParamAttr.Kind ==
12867 ParamAttr.Kind ==
12869 ParamAttr.Kind ==
12871 // Don't print the step value if it is not present or if it is
12872 // equal to 1.
12873 if (ParamAttr.StrideOrArg < 0)
12874 Out << 'n' << -ParamAttr.StrideOrArg;
12875 else if (ParamAttr.StrideOrArg != 1)
12876 Out << ParamAttr.StrideOrArg;
12877 }
12878
12879 if (!!ParamAttr.Alignment)
12880 Out << 'a' << ParamAttr.Alignment;
12881 }
12882
12883 return std::string(Out.str());
12884}
12885
12887 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
12889 struct ISADataTy {
12890 char ISA;
12891 unsigned VecRegSize;
12892 };
12893 ISADataTy ISAData[] = {
12894 {'b', 128}, // SSE
12895 {'c', 256}, // AVX
12896 {'d', 256}, // AVX2
12897 {'e', 512}, // AVX512
12898 };
12900 switch (Branch) {
12902 Masked.push_back('N');
12903 Masked.push_back('M');
12904 break;
12906 Masked.push_back('N');
12907 break;
12909 Masked.push_back('M');
12910 break;
12911 }
12912 for (char Mask : Masked) {
12913 for (const ISADataTy &Data : ISAData) {
12915 llvm::raw_svector_ostream Out(Buffer);
12916 Out << "_ZGV" << Data.ISA << Mask;
12917 if (!VLENVal) {
12918 assert(NumElts && "Non-zero simdlen/cdtsize expected");
12919 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
12920 } else {
12921 Out << VLENVal;
12922 }
12923 Out << mangleVectorParameters(ParamAttrs);
12924 Out << '_' << Fn->getName();
12925 Fn->addFnAttr(Out.str());
12926 }
12927 }
12928}
12929
12930// Function used to add the attribute. The parameter `VLEN` is templated to
12931// allow the use of `x` when targeting scalable functions for SVE.
12932template <typename T>
12933static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
12934 char ISA, StringRef ParSeq,
12935 StringRef MangledName, bool OutputBecomesInput,
12936 llvm::Function *Fn) {
12937 SmallString<256> Buffer;
12938 llvm::raw_svector_ostream Out(Buffer);
12939 Out << Prefix << ISA << LMask << VLEN;
12940 if (OutputBecomesInput)
12941 Out << 'v';
12942 Out << ParSeq << '_' << MangledName;
12943 Fn->addFnAttr(Out.str());
12944}
12945
12946// Helper function to generate the Advanced SIMD names depending on the value
12947// of the NDS when simdlen is not present.
12948static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
12949 StringRef Prefix, char ISA,
12950 StringRef ParSeq, StringRef MangledName,
12951 bool OutputBecomesInput,
12952 llvm::Function *Fn) {
12953 switch (NDS) {
12954 case 8:
12955 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12956 OutputBecomesInput, Fn);
12957 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
12958 OutputBecomesInput, Fn);
12959 break;
12960 case 16:
12961 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
12962 OutputBecomesInput, Fn);
12963 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
12964 OutputBecomesInput, Fn);
12965 break;
12966 case 32:
12967 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
12968 OutputBecomesInput, Fn);
12969 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
12970 OutputBecomesInput, Fn);
12971 break;
12972 case 64:
12973 case 128:
12974 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
12975 OutputBecomesInput, Fn);
12976 break;
12977 default:
12978 llvm_unreachable("Scalar type is too wide.");
12979 }
12980}
12981
12982/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
12984 llvm::Function *Fn, unsigned UserVLEN,
12986 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
12987 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
12988
12989 // Sort out parameter sequence.
12990 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
12991 StringRef Prefix = "_ZGV";
12992 StringRef MangledName = Fn->getName();
12993
12994 // Generate simdlen from user input (if any).
12995 if (UserVLEN) {
12996 if (ISA == 's') {
12997 // SVE generates only a masked function.
12998 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
12999 OutputBecomesInput, Fn);
13000 return;
13001 }
13002
13003 switch (Branch) {
13005 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13006 OutputBecomesInput, Fn);
13007 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13008 OutputBecomesInput, Fn);
13009 break;
13011 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13012 OutputBecomesInput, Fn);
13013 break;
13015 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13016 OutputBecomesInput, Fn);
13017 break;
13018 }
13019 return;
13020 }
13021
13022 if (ISA == 's') {
13023 // SVE, section 3.4.1, item 1.
13024 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13025 OutputBecomesInput, Fn);
13026 return;
13027 }
13028
13029 switch (Branch) {
13031 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13032 MangledName, OutputBecomesInput, Fn);
13033 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13034 MangledName, OutputBecomesInput, Fn);
13035 break;
13037 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13038 MangledName, OutputBecomesInput, Fn);
13039 break;
13041 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13042 MangledName, OutputBecomesInput, Fn);
13043 break;
13044 }
13045}
13046
13047//===----------------------------------------------------------------------===//
13048// OffloadEntriesInfoManager
13049//===----------------------------------------------------------------------===//
13050
13052 return OffloadEntriesTargetRegion.empty() &&
13053 OffloadEntriesDeviceGlobalVar.empty();
13054}
13055
13056unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13057 const TargetRegionEntryInfo &EntryInfo) const {
13058 auto It = OffloadEntriesTargetRegionCount.find(
13059 getTargetRegionEntryCountKey(EntryInfo));
13060 if (It == OffloadEntriesTargetRegionCount.end())
13061 return 0;
13062 return It->second;
13063}
13064
13065void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13066 const TargetRegionEntryInfo &EntryInfo) {
13067 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13068 EntryInfo.Count + 1;
13069}
13070
13071/// Initialize target region entry.
13073 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13074 OffloadEntriesTargetRegion[EntryInfo] =
13075 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13077 ++OffloadingEntriesNum;
13078}
13079
13081 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13083 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13084
13085 // Update the EntryInfo with the next available count for this location.
13086 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13087
13088 // If we are emitting code for a target, the entry is already initialized,
13089 // only has to be registered.
13090 if (OMPBuilder->Config.isTargetDevice()) {
13091 // This could happen if the device compilation is invoked standalone.
13092 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13093 return;
13094 }
13095 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13096 Entry.setAddress(Addr);
13097 Entry.setID(ID);
13098 Entry.setFlags(Flags);
13099 } else {
13101 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13102 return;
13103 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13104 "Target region entry already registered!");
13105 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13106 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13107 ++OffloadingEntriesNum;
13108 }
13109 incrementTargetRegionEntryInfoCount(EntryInfo);
13110}
13111
13113 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13114
13115 // Update the EntryInfo with the next available count for this location.
13116 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13117
13118 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13119 if (It == OffloadEntriesTargetRegion.end()) {
13120 return false;
13121 }
13122 // Fail if this entry is already registered.
13123 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13124 return false;
13125 return true;
13126}
13127
13129 const OffloadTargetRegionEntryInfoActTy &Action) {
13130 // Scan all target region entries and perform the provided action.
13131 for (const auto &It : OffloadEntriesTargetRegion) {
13132 Action(It.first, It.second);
13133 }
13134}
13135
13137 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13138 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13139 ++OffloadingEntriesNum;
13140}
13141
13143 StringRef VarName, Constant *Addr, int64_t VarSize,
13145 if (OMPBuilder->Config.isTargetDevice()) {
13146 // This could happen if the device compilation is invoked standalone.
13147 if (!hasDeviceGlobalVarEntryInfo(VarName))
13148 return;
13149 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13150 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13151 if (Entry.getVarSize() == 0) {
13152 Entry.setVarSize(VarSize);
13153 Entry.setLinkage(Linkage);
13154 }
13155 return;
13156 }
13157 Entry.setVarSize(VarSize);
13158 Entry.setLinkage(Linkage);
13159 Entry.setAddress(Addr);
13160 } else {
13161 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13162 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13163 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13164 "Entry not initialized!");
13165 if (Entry.getVarSize() == 0) {
13166 Entry.setVarSize(VarSize);
13167 Entry.setLinkage(Linkage);
13168 }
13169 return;
13170 }
13172 Flags ==
13174 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13175 Addr, VarSize, Flags, Linkage,
13176 VarName.str());
13177 else
13178 OffloadEntriesDeviceGlobalVar.try_emplace(
13179 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13180 ++OffloadingEntriesNum;
13181 }
13182}
13183
13186 // Scan all target region entries and perform the provided action.
13187 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13188 Action(E.getKey(), E.getValue());
13189}
13190
13191//===----------------------------------------------------------------------===//
13192// CanonicalLoopInfo
13193//===----------------------------------------------------------------------===//
13194
13195void CanonicalLoopInfo::collectControlBlocks(
13197 // We only count those BBs as control block for which we do not need to
13198 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13199 // flow. For consistency, this also means we do not add the Body block, which
13200 // is just the entry to the body code.
13201 BBs.reserve(BBs.size() + 6);
13202 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13203}
13204
13206 assert(isValid() && "Requires a valid canonical loop");
13207 for (BasicBlock *Pred : predecessors(Header)) {
13208 if (Pred != Latch)
13209 return Pred;
13210 }
13211 llvm_unreachable("Missing preheader");
13212}
13213
13214void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13215 assert(isValid() && "Requires a valid canonical loop");
13216
13217 Instruction *CmpI = &getCond()->front();
13218 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13219 CmpI->setOperand(1, TripCount);
13220
13221#ifndef NDEBUG
13222 assertOK();
13223#endif
13224}
13225
13226void CanonicalLoopInfo::mapIndVar(
13227 llvm::function_ref<Value *(Instruction *)> Updater) {
13228 assert(isValid() && "Requires a valid canonical loop");
13229
13230 Instruction *OldIV = getIndVar();
13231
13232 // Record all uses excluding those introduced by the updater. Uses by the
13233 // CanonicalLoopInfo itself to keep track of the number of iterations are
13234 // excluded.
13235 SmallVector<Use *> ReplacableUses;
13236 for (Use &U : OldIV->uses()) {
13237 auto *User = dyn_cast<Instruction>(U.getUser());
13238 if (!User)
13239 continue;
13240 if (User->getParent() == getCond())
13241 continue;
13242 if (User->getParent() == getLatch())
13243 continue;
13244 ReplacableUses.push_back(&U);
13245 }
13246
13247 // Run the updater that may introduce new uses
13248 Value *NewIV = Updater(OldIV);
13249
13250 // Replace the old uses with the value returned by the updater.
13251 for (Use *U : ReplacableUses)
13252 U->set(NewIV);
13253
13254#ifndef NDEBUG
13255 assertOK();
13256#endif
13257}
13258
13260#ifndef NDEBUG
13261 // No constraints if this object currently does not describe a loop.
13262 if (!isValid())
13263 return;
13264
13265 BasicBlock *Preheader = getPreheader();
13266 BasicBlock *Body = getBody();
13267 BasicBlock *After = getAfter();
13268
13269 // Verify standard control-flow we use for OpenMP loops.
13270 assert(Preheader);
13271 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13272 "Preheader must terminate with unconditional branch");
13273 assert(Preheader->getSingleSuccessor() == Header &&
13274 "Preheader must jump to header");
13275
13276 assert(Header);
13277 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13278 "Header must terminate with unconditional branch");
13279 assert(Header->getSingleSuccessor() == Cond &&
13280 "Header must jump to exiting block");
13281
13282 assert(Cond);
13283 assert(Cond->getSinglePredecessor() == Header &&
13284 "Exiting block only reachable from header");
13285
13286 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13287 "Exiting block must terminate with conditional branch");
13288 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13289 "Exiting block's first successor jump to the body");
13290 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13291 "Exiting block's second successor must exit the loop");
13292
13293 assert(Body);
13294 assert(Body->getSinglePredecessor() == Cond &&
13295 "Body only reachable from exiting block");
13296 assert(!isa<PHINode>(Body->front()));
13297
13298 assert(Latch);
13299 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13300 "Latch must terminate with unconditional branch");
13301 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13302 // TODO: To support simple redirecting of the end of the body code that has
13303 // multiple; introduce another auxiliary basic block like preheader and after.
13304 assert(Latch->getSinglePredecessor() != nullptr);
13305 assert(!isa<PHINode>(Latch->front()));
13306
13307 assert(Exit);
13308 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13309 "Exit block must terminate with unconditional branch");
13310 assert(Exit->getSingleSuccessor() == After &&
13311 "Exit block must jump to after block");
13312
13313 assert(After);
13314 assert(After->getSinglePredecessor() == Exit &&
13315 "After block only reachable from exit block");
13316 assert(After->empty() || !isa<PHINode>(After->front()));
13317
13318 Instruction *IndVar = getIndVar();
13319 assert(IndVar && "Canonical induction variable not found?");
13320 assert(isa<IntegerType>(IndVar->getType()) &&
13321 "Induction variable must be an integer");
13322 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13323 "Induction variable must be a PHI in the loop header");
13324 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13325 assert(
13326 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13327 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13328
13329 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13330 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13331 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13332 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13333 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13334 ->isOne());
13335
13336 Value *TripCount = getTripCount();
13337 assert(TripCount && "Loop trip count not found?");
13338 assert(IndVar->getType() == TripCount->getType() &&
13339 "Trip count and induction variable must have the same type");
13340
13341 auto *CmpI = cast<CmpInst>(&Cond->front());
13342 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13343 "Exit condition must be a signed less-than comparison");
13344 assert(CmpI->getOperand(0) == IndVar &&
13345 "Exit condition must compare the induction variable");
13346 assert(CmpI->getOperand(1) == TripCount &&
13347 "Exit condition must compare with the trip count");
13348#endif
13349}
13350
13352 Header = nullptr;
13353 Cond = nullptr;
13354 Latch = nullptr;
13355 Exit = nullptr;
13356}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Rewrite undef for PHI
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static cl::opt< ITMode > IT(cl::desc("IT block support"), cl::Hidden, cl::init(DefaultIT), cl::values(clEnumValN(DefaultIT, "arm-default-it", "Generate any type of IT block"), clEnumValN(RestrictedIT, "arm-restrict-it", "Disallow complex IT blocks")))
Expand Atomic instructions
@ ParamAttr
This file contains the simple types necessary to represent the attributes associated with functions a...
static const Function * getParent(const Value *V)
#define X(NUM, ENUM, NAME)
Definition ELF.h:856
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Hexagon Common GEP
Hexagon Hardware Loops
This file provides various utilities for inspecting and working with the control flow graph in LLVM I...
This header defines various interfaces for pass management in LLVM.
iv Induction Variable Users
Definition IVUsers.cpp:48
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
static LVOptions Options
Definition LVOptions.cpp:25
static bool isZero(Value *V, const DataLayout &DL, DominatorTree *DT, AssumptionCache *AC)
Definition Lint.cpp:539
static cl::opt< unsigned > TileSize("fuse-matrix-tile-size", cl::init(4), cl::Hidden, cl::desc("Tile size for matrix instruction fusion using square-shaped tiles."))
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
This file contains the declarations for metadata subclasses.
#define T
uint64_t IntrinsicInst * II
#define OMP_KERNEL_ARG_VERSION
Provides definitions for Target specific Grid Values.
static Value * removeASCastIfPresent(Value *V)
static void createTargetLoopWorkshareCall(OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType, BasicBlock *InsertBlock, Value *Ident, Value *LoopBodyArg, Value *TripCount, Function &LoopBodyFn, bool NoLoop)
Value * createFakeIntVal(IRBuilderBase &Builder, OpenMPIRBuilder::InsertPointTy OuterAllocaIP, llvm::SmallVectorImpl< Instruction * > &ToBeDeleted, OpenMPIRBuilder::InsertPointTy InnerAllocaIP, const Twine &Name="", bool AsPtr=true, bool Is64Bit=false)
static Function * createTargetParallelWrapper(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn)
Create wrapper function used to gather the outlined function's argument structure from a shared buffe...
static void redirectTo(BasicBlock *Source, BasicBlock *Target, DebugLoc DL)
Make Source branch to Target.
static FunctionCallee getKmpcDistForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void applyParallelAccessesMetadata(CanonicalLoopInfo *CLI, LLVMContext &Ctx, Loop *Loop, LoopInfo &LoopInfo, SmallVector< Metadata * > &LoopMDList)
static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix, char ISA, StringRef ParSeq, StringRef MangledName, bool OutputBecomesInput, llvm::Function *Fn)
static FunctionCallee getKmpcForDynamicFiniForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for finalizing the dynamic loop using depending on type.
static Expected< Function * > createOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, StringRef FuncName, SmallVectorImpl< Value * > &Inputs, OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc, OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB)
static void FixupDebugInfoForOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func, DenseMap< Value *, std::tuple< Value *, unsigned > > &ValueReplacementMap)
static OMPScheduleType getOpenMPOrderingScheduleType(OMPScheduleType BaseScheduleType, bool HasOrderedClause)
Adds ordering modifier flags to schedule type.
static OMPScheduleType getOpenMPMonotonicityScheduleType(OMPScheduleType ScheduleType, bool HasSimdModifier, bool HasMonotonic, bool HasNonmonotonic, bool HasOrderedClause)
Adds monotonicity modifier flags to schedule type.
static std::string mangleVectorParameters(ArrayRef< llvm::OpenMPIRBuilder::DeclareSimdAttrTy > ParamAttrs)
Mangle the parameter part of the vector function name according to their OpenMP classification.
static bool isGenericKernel(Function &Fn)
static void workshareLoopTargetCallback(OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident, Function &OutlinedFn, const SmallVector< Instruction *, 4 > &ToBeDeleted, WorksharingLoopType LoopType, bool NoLoop)
static bool isValidWorkshareLoopScheduleType(OMPScheduleType SchedType)
static bool isAtomicableReductionSet(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos)
static llvm::CallInst * emitNoUnwindRuntimeCall(IRBuilder<> &Builder, llvm::FunctionCallee Callee, ArrayRef< llvm::Value * > Args, const llvm::Twine &Name)
static Error populateReductionFunction(Function *ReductionFunc, ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, IRBuilder<> &Builder, ArrayRef< bool > IsByRef, bool IsGPU)
static Function * getFreshReductionFunc(Module &M)
static void raiseUserConstantDataAllocasToEntryBlock(IRBuilderBase &Builder, Function *Function)
static FunctionCallee getKmpcForDynamicNextForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for updating the next loop using OpenMP dynamic scheduling depending...
static bool isConflictIP(IRBuilder<>::InsertPoint IP1, IRBuilder<>::InsertPoint IP2)
Return whether IP1 and IP2 are ambiguous, i.e.
static void checkReductionInfos(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, bool IsGPU)
static Type * getOffloadingArrayType(Value *V)
static OMPScheduleType getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasDistScheduleChunks)
Determine which scheduling algorithm to use, determined from schedule clause arguments.
static OMPScheduleType computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasMonotonicModifier, bool HasNonmonotonicModifier, bool HasOrderedClause, bool HasDistScheduleChunks)
Determine the schedule type using schedule and ordering clause arguments.
static FunctionCallee getKmpcForDynamicInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for initializing loop bounds using OpenMP dynamic scheduling dependi...
static std::optional< omp::OMPTgtExecModeFlags > getTargetKernelExecMode(Function &Kernel)
Given a function, if it represents the entry point of a target kernel, this returns the execution mod...
static StructType * createTaskWithPrivatesTy(OpenMPIRBuilder &OMPIRBuilder, ArrayRef< Value * > OffloadingArraysToPrivatize)
static cl::opt< double > UnrollThresholdFactor("openmp-ir-builder-unroll-threshold-factor", cl::Hidden, cl::desc("Factor for the unroll threshold to account for code " "simplifications still taking place"), cl::init(1.5))
static cl::opt< bool > UseDefaultMaxThreads("openmp-ir-builder-use-default-max-threads", cl::Hidden, cl::desc("Use a default max threads if none is provided."), cl::init(true))
static int32_t computeHeuristicUnrollFactor(CanonicalLoopInfo *CLI)
Heuristically determine the best-performant unroll factor for CLI.
static void emitTargetCall(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, OpenMPIRBuilder::InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, OpenMPIRBuilder::TargetDataInfo &Info, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, const OpenMPIRBuilder::TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID, SmallVectorImpl< Value * > &Args, OpenMPIRBuilder::GenMapInfoCallbackTy GenMapInfoCB, OpenMPIRBuilder::CustomMapperCallbackTy CustomMapperCB, const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait, Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback)
static Value * emitTaskDependencies(OpenMPIRBuilder &OMPBuilder, const SmallVectorImpl< OpenMPIRBuilder::DependData > &Dependencies)
static Error emitTargetOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry, TargetRegionEntryInfo &EntryInfo, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, Function *&OutlinedFn, Constant *&OutlinedFnID, SmallVectorImpl< Value * > &Inputs, OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc, OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB)
static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value, bool Min)
static OpenMPIRBuilder::InsertPointTy getInsertPointAfterInstr(Instruction *I)
static void redirectAllPredecessorsTo(BasicBlock *OldTarget, BasicBlock *NewTarget, DebugLoc DL)
Redirect all edges that branch to OldTarget to NewTarget.
static void hoistNonEntryAllocasToEntryBlock(llvm::BasicBlock &Block)
static std::unique_ptr< TargetMachine > createTargetMachine(Function *F, CodeGenOptLevel OptLevel)
Create the TargetMachine object to query the backend for optimization preferences.
static FunctionCallee getKmpcForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup, LoopInfo &LI)
Attach llvm.access.group metadata to the memref instructions of Block.
static void addBasicBlockMetadata(BasicBlock *BB, ArrayRef< Metadata * > Properties)
Attach metadata Properties to the basic block described by BB.
static void restoreIPandDebugLoc(llvm::IRBuilderBase &Builder, llvm::IRBuilderBase::InsertPoint IP)
This is a wrapper over IRBuilderBase::restoreIP that also restores a current debug location when the ...
static LoadInst * loadSharedDataFromTaskDescriptor(OpenMPIRBuilder &OMPIRBuilder, IRBuilderBase &Builder, Value *TaskWithPrivates, Type *TaskWithPrivatesTy)
Given a task descriptor, TaskWithPrivates, return the pointer to the block of pointers containing sha...
static cl::opt< bool > OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden, cl::desc("Use optimistic attributes describing " "'as-if' properties of runtime calls."), cl::init(false))
static bool hasGridValue(const Triple &T)
static FunctionCallee getKmpcForStaticLoopForType(Type *Ty, OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType)
static const omp::GV & getGridValue(const Triple &T, Function *Kernel)
static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask, StringRef Prefix, char ISA, StringRef ParSeq, StringRef MangledName, bool OutputBecomesInput, llvm::Function *Fn)
static Function * emitTargetTaskProxyFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI, StructType *PrivatesTy, StructType *TaskWithPrivatesTy, const size_t NumOffloadingArrays, const int SharedArgsOperandNo)
Create an entry point for a target task with the following.
static void addLoopMetadata(CanonicalLoopInfo *Loop, ArrayRef< Metadata * > Properties)
Attach loop metadata Properties to the loop described by Loop.
static AtomicOrdering TransformReleaseAcquireRelease(AtomicOrdering AO)
static void removeUnusedBlocksFromParent(ArrayRef< BasicBlock * > BBs)
static void targetParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition, Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr, Value *ThreadID, const SmallVector< Instruction *, 4 > &ToBeDeleted)
static void hostParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, Value *Ident, Value *IfCondition, Instruction *PrivTID, AllocaInst *PrivTIDAddr, const SmallVector< Instruction *, 4 > &ToBeDeleted)
#define P(N)
FunctionAnalysisManager FAM
Function * Fun
This file defines the Pass Instrumentation classes that provide instrumentation points into the pass ...
const SmallVectorImpl< MachineOperand > & Cond
Remove Loads Into Fake Uses
static bool isValid(const char C)
Returns true if C is a valid mangled character: <0-9a-zA-Z_>.
SmallPtrSet< BasicBlock *, 0 > BlockSet
This file implements the SmallBitVector class.
This file defines the SmallSet class.
This file defines less commonly used SmallVector utilities.
This file contains some functions that are useful when dealing with strings.
#define LLVM_DEBUG(...)
Definition Debug.h:119
static SymbolRef::Type getType(const Symbol *Sym)
Definition TapiFile.cpp:39
Defines the virtual file system interface vfs::FileSystem.
Value * RHS
Value * LHS
static cl::opt< unsigned > MaxThreads("xcore-max-threads", cl::Optional, cl::desc("Maximum number of threads (for emulation thread-local storage)"), cl::Hidden, cl::value_desc("number"), cl::init(8))
static const uint32_t IV[8]
Definition blake3_impl.h:83
The Input class is used to parse a yaml document into in-memory structs and vectors.
Class for arbitrary precision integers.
Definition APInt.h:78
static APInt getSignedMaxValue(unsigned numBits)
Gets maximum signed value of APInt for a specific bit width.
Definition APInt.h:210
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:637
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition Function.h:168
const BasicBlock & getEntryBlock() const
Definition Function.h:793
Argument * arg_iterator
Definition Function.h:73
bool empty() const
Definition Function.h:843
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Definition Function.h:211
void removeFromParent()
removeFromParent - This method unlinks 'this' from the containing module, but does not delete it.
Definition Function.cpp:444
const DataLayout & getDataLayout() const
Get the data layout of the module this function belongs to.
Definition Function.cpp:357
Attribute getFnAttribute(Attribute::AttrKind Kind) const
Return the attribute for the given attribute kind.
Definition Function.cpp:762
DISubprogram * getSubprogram() const
Get the attached subprogram.
AttributeList getAttributes() const
Return the attribute list for this Function.
Definition Function.h:328
const Function & getFunction() const
Definition Function.h:166
iterator begin()
Definition Function.h:837
arg_iterator arg_begin()
Definition Function.h:852
void setAttributes(AttributeList Attrs)
Set the attribute list for this Function.
Definition Function.h:331
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:353
void addParamAttr(unsigned ArgNo, Attribute::AttrKind Kind)
adds the attribute to the list of attributes for the given arg.
Definition Function.cpp:665
Function::iterator insert(Function::iterator Position, BasicBlock *BB)
Insert BB in the basic block list at Position.
Definition Function.h:739
size_t arg_size() const
Definition Function.h:885
Type * getReturnType() const
Returns the type of the ret val.
Definition Function.h:216
iterator end()
Definition Function.h:839
void setCallingConv(CallingConv::ID CC)
Definition Function.h:276
Argument * getArg(unsigned i) const
Definition Function.h:870
bool hasMetadata() const
Return true if this GlobalObject has any metadata attached to it.
LLVM_ABI void addMetadata(unsigned KindID, MDNode &MD)
Add a metadata attachment.
LinkageTypes getLinkage() const
void setLinkage(LinkageTypes LT)
Module * getParent()
Get the module that this global value is contained inside of...
void setDSOLocal(bool Local)
PointerType * getType() const
Global values are always pointers.
@ HiddenVisibility
The GV is hidden.
Definition GlobalValue.h:69
@ ProtectedVisibility
The GV is protected.
Definition GlobalValue.h:70
void setVisibility(VisibilityTypes V)
LinkageTypes
An enumeration for the kinds of linkage for global values.
Definition GlobalValue.h:52
@ PrivateLinkage
Like Internal, but omit from symbol table.
Definition GlobalValue.h:61
@ CommonLinkage
Tentative definitions.
Definition GlobalValue.h:63
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
@ WeakODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:58
@ WeakAnyLinkage
Keep one copy of named function when linking (weak)
Definition GlobalValue.h:57
@ AppendingLinkage
Special purpose, only applies to global arrays.
Definition GlobalValue.h:59
@ LinkOnceODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:56
Type * getValueType() const
const Constant * getInitializer() const
getInitializer - Return the initializer for this global variable.
InsertPoint - A saved insertion point.
Definition IRBuilder.h:246
BasicBlock * getBlock() const
Definition IRBuilder.h:261
bool isSet() const
Returns true if this insert point is set.
Definition IRBuilder.h:259
BasicBlock::iterator getPoint() const
Definition IRBuilder.h:262
Common base class shared among various IRBuilders.
Definition IRBuilder.h:114
InsertPoint saveIP() const
Returns the current insert point.
Definition IRBuilder.h:266
void restoreIP(InsertPoint IP)
Sets the current insert point to a previously-saved location.
Definition IRBuilder.h:278
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2893
LLVM_ABI const DebugLoc & getStableDebugLoc() const
Fetch the debug location for this node, unless this is a debug intrinsic, in which case fetch the deb...
LLVM_ABI void removeFromParent()
This method unlinks 'this' from the containing basic block, but does not delete it.
LLVM_ABI unsigned getNumSuccessors() const LLVM_READONLY
Return the number of successors that this instruction has.
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
LLVM_ABI const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
LLVM_ABI void moveBefore(InstListType::iterator InsertPos)
Unlink this instruction from its current basic block and insert it into the basic block that MovePos ...
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
MDNode * getMetadata(unsigned KindID) const
Get the metadata of given kind attached to this Instruction.
LLVM_ABI BasicBlock * getSuccessor(unsigned Idx) const LLVM_READONLY
Return the specified successor. This instruction must be a terminator.
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set the metadata of the specified kind to the specified node.
LLVM_ABI void moveBeforePreserving(InstListType::iterator MovePos)
Perform a moveBefore operation, while signalling that the caller intends to preserve the original ord...
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
LLVM_ABI void insertAfter(Instruction *InsertPos)
Insert an unlinked instruction into a basic block immediately after the specified instruction.
Class to represent integer types.
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:348
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
An instruction for reading from memory.
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this load instruction.
Align getAlign() const
Return the alignment of the access that is being performed.
Analysis pass that exposes the LoopInfo for a function.
Definition LoopInfo.h:587
LLVM_ABI LoopInfo run(Function &F, FunctionAnalysisManager &AM)
ArrayRef< BlockT * > getBlocks() const
Get a list of the basic blocks which make up this loop.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
This class represents a loop nest and can be used to query its properties.
Represents a single loop in the control flow graph.
Definition LoopInfo.h:40
LLVM_ABI MDNode * createCallbackEncoding(unsigned CalleeArgNo, ArrayRef< int > Arguments, bool VarArgsArePassed)
Return metadata describing a callback (see llvm::AbstractCallSite).
Metadata node.
Definition Metadata.h:1069
LLVM_ABI void replaceOperandWith(unsigned I, Metadata *New)
Replace a specific operand.
static MDTuple * getDistinct(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1575
ArrayRef< MDOperand > operands() const
Definition Metadata.h:1424
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1567
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Definition Metadata.cpp:615
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:38
size_type size() const
Definition MapVector.h:58
Root of the metadata hierarchy.
Definition Metadata.h:64
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
LLVMContext & getContext() const
Get the global data context.
Definition Module.h:327
const DataLayout & getDataLayout() const
Get the data layout for the module's target platform.
Definition Module.h:320
A tuple of MDNodes.
Definition Metadata.h:1755
iterator_range< op_iterator > operands()
Definition Metadata.h:1851
LLVM_ABI void addOperand(MDNode *M)
Class that manages information about offload code regions and data.
function_ref< void(StringRef, const OffloadEntryInfoDeviceGlobalVar &)> OffloadDeviceGlobalVarEntryInfoActTy
Applies action Action on all registered entries.
OMPTargetDeviceClauseKind
Kind of device clause for declare target variables and functions NOTE: Currently not used as a part o...
@ OMPTargetDeviceClauseAny
The target is marked for all devices.
LLVM_ABI void registerDeviceGlobalVarEntryInfo(StringRef VarName, Constant *Addr, int64_t VarSize, OMPTargetGlobalVarEntryKind Flags, GlobalValue::LinkageTypes Linkage)
Register device global variable entry.
LLVM_ABI void initializeDeviceGlobalVarEntryInfo(StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order)
Initialize device global variable entry.
LLVM_ABI void actOnDeviceGlobalVarEntriesInfo(const OffloadDeviceGlobalVarEntryInfoActTy &Action)
OMPTargetRegionEntryKind
Kind of the target registry entry.
@ OMPTargetRegionEntryTargetRegion
Mark the entry as target region.
LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, const TargetRegionEntryInfo &EntryInfo)
LLVM_ABI bool hasTargetRegionEntryInfo(TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId=false) const
Return true if a target region entry with the provided information exists.
LLVM_ABI void registerTargetRegionEntryInfo(TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID, OMPTargetRegionEntryKind Flags)
Register target region entry.
LLVM_ABI void actOnTargetRegionEntriesInfo(const OffloadTargetRegionEntryInfoActTy &Action)
LLVM_ABI void initializeTargetRegionEntryInfo(const TargetRegionEntryInfo &EntryInfo, unsigned Order)
Initialize target region entry.
OMPTargetGlobalVarEntryKind
Kind of the global variable entry..
@ OMPTargetGlobalVarEntryEnter
Mark the entry as a declare target enter.
@ OMPTargetGlobalRegisterRequires
Mark the entry as a register requires global.
@ OMPTargetGlobalVarEntryIndirect
Mark the entry as a declare target indirect global.
@ OMPTargetGlobalVarEntryLink
Mark the entry as a to declare target link.
@ OMPTargetGlobalVarEntryTo
Mark the entry as a to declare target.
@ OMPTargetGlobalVarEntryIndirectVTable
Mark the entry as a declare target indirect vtable.
function_ref< void(const TargetRegionEntryInfo &EntryInfo, const OffloadEntryInfoTargetRegion &)> OffloadTargetRegionEntryInfoActTy
brief Applies action Action on all registered entries.
bool hasDeviceGlobalVarEntryInfo(StringRef VarName) const
Checks if the variable with the given name has been registered already.
LLVM_ABI bool empty() const
Return true if a there are no entries defined.
std::optional< bool > IsTargetDevice
Flag to define whether to generate code for the role of the OpenMP host (if set to false) or device (...
std::optional< bool > IsGPU
Flag for specifying if the compilation is done for an accelerator.
LLVM_ABI int64_t getRequiresFlags() const
Returns requires directive clauses as flags compatible with those expected by libomptarget.
std::optional< bool > OpenMPOffloadMandatory
Flag for specifying if offloading is mandatory.
LLVM_ABI void setHasRequiresReverseOffload(bool Value)
LLVM_ABI bool hasRequiresUnifiedSharedMemory() const
LLVM_ABI void setHasRequiresUnifiedSharedMemory(bool Value)
unsigned getDefaultTargetAS() const
LLVM_ABI bool hasRequiresDynamicAllocators() const
LLVM_ABI void setHasRequiresUnifiedAddress(bool Value)
LLVM_ABI void setHasRequiresDynamicAllocators(bool Value)
LLVM_ABI bool hasRequiresReverseOffload() const
LLVM_ABI bool hasRequiresUnifiedAddress() const
Struct that keeps the information that should be kept throughout a 'target data' region.
An interface to create LLVM-IR for OpenMP directives.
LLVM_ABI InsertPointOrErrorTy createOrderedThreadsSimd(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsThreads)
Generator for 'omp ordered [threads | simd]'.
LLVM_ABI void emitAArch64DeclareSimdFunction(llvm::Function *Fn, unsigned VLENVal, llvm::ArrayRef< DeclareSimdAttrTy > ParamAttrs, DeclareSimdBranch Branch, char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput)
Emit AArch64 vector-function ABI attributes for a declare simd function.
LLVM_ABI Constant * getOrCreateIdent(Constant *SrcLocStr, uint32_t SrcLocStrSize, omp::IdentFlag Flags=omp::IdentFlag(0), unsigned Reserve2Flags=0)
Return an ident_t* encoding the source location SrcLocStr and Flags.
LLVM_ABI void registerDeclareTargetGlobalReplacement(GlobalValue *Original, GlobalValue *Replacement)
Register a module-scope replacement of a declare target global variable.
LLVM_ABI FunctionCallee getOrCreateRuntimeFunction(Module &M, omp::RuntimeFunction FnID)
Return the function declaration for the runtime function with FnID.
LLVM_ABI InsertPointOrErrorTy createCancel(const LocationDescription &Loc, Value *IfCondition, omp::Directive CanceledDirective)
Generator for 'omp cancel'.
std::function< Expected< Function * >(StringRef FunctionName)> FunctionGenCallback
Functions used to generate a function with the given name.
LLVM_ABI CallInst * createOMPAllocShared(const LocationDescription &Loc, Value *Size, const Twine &Name=Twine(""))
Create a runtime call for kmpc_alloc_shared.
ReductionGenCBKind
Enum class for the RedctionGen CallBack type to be used.
LLVM_ABI CanonicalLoopInfo * collapseLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops, InsertPointTy ComputeIP)
Collapse a loop nest into a single loop.
LLVM_ABI void createTaskyield(const LocationDescription &Loc)
Generator for 'omp taskyield'.
std::function< Error(InsertPointTy CodeGenIP)> FinalizeCallbackTy
Callback type for variable finalization (think destructors).
LLVM_ABI void emitBranch(BasicBlock *Target)
LLVM_ABI Error emitCancelationCheckImpl(Value *CancelFlag, omp::Directive CanceledDirective)
Generate control flow and cleanup for cancellation.
static LLVM_ABI void writeThreadBoundsForKernel(const Triple &T, Function &Kernel, int32_t LB, int32_t UB)
LLVM_ABI void emitTaskwaitImpl(const LocationDescription &Loc)
Generate a taskwait runtime call.
LLVM_ABI Constant * registerTargetRegionFunction(TargetRegionEntryInfo &EntryInfo, Function *OutlinedFunction, StringRef EntryFnName, StringRef EntryFnIDName)
Registers the given function and sets up the attribtues of the function Returns the FunctionID.
LLVM_ABI GlobalVariable * emitKernelExecutionMode(StringRef KernelName, omp::OMPTgtExecModeFlags Mode)
Emit the kernel execution mode.
LLVM_ABI void initialize()
Initialize the internal state, this will put structures types and potentially other helpers into the ...
LLVM_ABI InsertPointTy createAtomicCompare(const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V, AtomicOpValue &R, Value *E, Value *D, AtomicOrdering AO, omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate, bool IsFailOnly, bool IsWeak=false)
LLVM_ABI InsertPointTy createAtomicWrite(const LocationDescription &Loc, AtomicOpValue &X, Value *Expr, AtomicOrdering AO, InsertPointTy AllocaIP)
Emit atomic write for : X = Expr — Only Scalar data types.
LLVM_ABI void loadOffloadInfoMetadata(Module &M)
Loads all the offload entries information from the host IR metadata.
function_ref< MapInfosTy &(InsertPointTy CodeGenIP)> GenMapInfoCallbackTy
Callback type for creating the map infos for the kernel parameters.
LLVM_ABI Error emitOffloadingArrays(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo, TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous=false, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr)
Emit the arrays used to pass the captures and map information to the offloading runtime library.
LLVM_ABI void unrollLoopFull(DebugLoc DL, CanonicalLoopInfo *Loop)
Fully unroll a loop.
function_ref< Error(InsertPointTy CodeGenIP, Value *IndVar)> LoopBodyGenCallbackTy
Callback type for loop body code generation.
LLVM_ABI InsertPointOrErrorTy emitScanReduction(const LocationDescription &Loc, ArrayRef< llvm::OpenMPIRBuilder::ReductionInfo > ReductionInfos, ScanInfo *ScanRedInfo)
This function performs the scan reduction of the values updated in the input phase.
LLVM_ABI void emitFlush(const LocationDescription &Loc)
Generate a flush runtime call.
LLVM_ABI InsertPointOrErrorTy createScope(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsNowait)
Generator for 'omp scope'.
static LLVM_ABI std::pair< int32_t, int32_t > readThreadBoundsForKernel(const Triple &T, Function &Kernel)
}
OpenMPIRBuilderConfig Config
The OpenMPIRBuilder Configuration.
LLVM_ABI CallInst * createOMPInteropDestroy(const LocationDescription &Loc, Value *InteropVar, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_destroy.
LLVM_ABI void emitUsed(StringRef Name, ArrayRef< llvm::WeakTrackingVH > List)
Emit the llvm.used metadata.
LLVM_ABI InsertPointOrErrorTy createSingle(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef< llvm::Value * > CPVars={}, ArrayRef< llvm::Function * > CPFuncs={})
Generator for 'omp single'.
LLVM_ABI InsertPointOrErrorTy createTeams(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower=nullptr, Value *NumTeamsUpper=nullptr, Value *ThreadLimit=nullptr, Value *IfExpr=nullptr)
Generator for #omp teams
std::forward_list< CanonicalLoopInfo > LoopInfos
Collection of owned canonical loop objects that eventually need to be free'd.
LLVM_ABI llvm::StructType * getKmpTaskAffinityInfoTy()
Return the LLVM struct type matching runtime kmp_task_affinity_info_t.
LLVM_ABI std::string createPlatformSpecificName(ArrayRef< StringRef > Parts) const
Get the create a name using the platform specific separators.
LLVM_ABI FunctionCallee createDispatchNextFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_next_* runtime function for the specified size IVSize and sign IVSigned.
static LLVM_ABI void getKernelArgsVector(TargetKernelArgs &KernelArgs, IRBuilderBase &Builder, SmallVector< Value * > &ArgsVector)
Create the kernel args vector used by emitTargetKernel.
LLVM_ABI InsertPointOrErrorTy createTarget(const LocationDescription &Loc, bool IsOffloadEntry, OpenMPIRBuilder::InsertPointTy AllocaIP, OpenMPIRBuilder::InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks, TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo, const TargetKernelDefaultAttrs &DefaultAttrs, const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond, SmallVectorImpl< Value * > &Inputs, GenMapInfoCallbackTy GenMapInfoCB, TargetBodyGenCallbackTy BodyGenCB, TargetGenArgAccessorsCallbackTy ArgAccessorFuncCB, CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies={}, bool HasNowait=false, Value *DynCGroupMem=nullptr, omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback=omp::OMPDynGroupprivateFallbackType::Abort)
Generator for 'omp target'.
LLVM_ABI void unrollLoopHeuristic(DebugLoc DL, CanonicalLoopInfo *Loop)
Fully or partially unroll a loop.
LLVM_ABI omp::OpenMPOffloadMappingFlags getMemberOfFlag(unsigned Position)
Get OMP_MAP_MEMBER_OF flag with extra bits reserved based on the position given.
LLVM_ABI void addAttributes(omp::RuntimeFunction FnID, Function &Fn)
Add attributes known for FnID to Fn.
Module & M
The underlying LLVM-IR module.
StringMap< Constant * > SrcLocStrMap
Map to remember source location strings.
LLVM_ABI void createMapperAllocas(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumOperands, struct MapperAllocas &MapperAllocas)
Create the allocas instruction used in call to mapper functions.
SmallVector< DeclareTargetGlobalReplacement, 8 > DeclareTargetGlobalReplacements
Collection of declare target globals to rewrite uses of during device module finalizaiton.
LLVM_ABI Constant * getOrCreateSrcLocStr(StringRef LocStr, uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the source location LocStr.
LLVM_ABI Error emitTargetRegionFunction(TargetRegionEntryInfo &EntryInfo, FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry, Function *&OutlinedFn, Constant *&OutlinedFnID)
Create a unique name for the entry function using the source location information of the current targ...
LLVM_ABI InsertPointOrErrorTy createIteratorLoop(LocationDescription Loc, llvm::Value *TripCount, IteratorBodyGenTy BodyGen, llvm::StringRef Name="iterator")
Create a canonical iterator loop at the current insertion point.
LLVM_ABI Expected< SmallVector< llvm::CanonicalLoopInfo * > > createCanonicalScanLoops(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo)
Generator for the control flow structure of an OpenMP canonical loops if the parent directive has an ...
LLVM_ABI FunctionCallee createDispatchFiniFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_fini_* runtime function for the specified size IVSize and sign IVSigned.
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> TargetBodyGenCallbackTy
LLVM_ABI void unrollLoopPartial(DebugLoc DL, CanonicalLoopInfo *Loop, int32_t Factor, CanonicalLoopInfo **UnrolledCLI)
Partially unroll a loop.
function_ref< Error(Value *DeviceID, Value *RTLoc, IRBuilderBase::InsertPoint TargetTaskAllocaIP)> TargetTaskBodyCallbackTy
Callback type for generating the bodies of device directives that require outer target tasks (e....
Expected< MapInfosTy & > MapInfosOrErrorTy
bool HandleFPNegZero
Emit atomic compare for constructs: — Only scalar data types cond-expr-stmt: x = x ordop expr ?
LLVM_ABI void emitTaskyieldImpl(const LocationDescription &Loc)
Generate a taskyield runtime call.
LLVM_ABI void emitMapperCall(const LocationDescription &Loc, Function *MapperFunc, Value *SrcLocInfo, Value *MaptypesArg, Value *MapnamesArg, struct MapperAllocas &MapperAllocas, int64_t DeviceID, unsigned NumOperands)
Create the call for the target mapper function.
LLVM_ABI InsertPointOrErrorTy createDistribute(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB)
Generator for #omp distribute
LLVM_ABI InsertPointOrErrorTy createTask(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB, bool Tied=true, Value *Final=nullptr, Value *IfCondition=nullptr, const DependenciesInfo &Dependencies={}, const AffinityData &Affinities={}, bool Mergeable=false, Value *EventHandle=nullptr, Value *Priority=nullptr)
Generator for #omp taskloop
function_ref< Expected< Function * >(unsigned int)> CustomMapperCallbackTy
LLVM_ABI InsertPointTy createOrderedDepend(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumLoops, ArrayRef< llvm::Value * > StoreValues, const Twine &Name, bool IsDependSource)
Generator for 'omp ordered depend (source | sink)'.
LLVM_ABI InsertPointTy createCopyinClauseBlocks(InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr, llvm::IntegerType *IntPtrTy, bool BranchtoEnd=true)
Generate conditional branch and relevant BasicBlocks through which private threads copy the 'copyin' ...
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &Original, Value &Inner, Value *&ReplVal)> PrivatizeCallbackTy
Callback type for variable privatization (think copy & default constructor).
LLVM_ABI bool isFinalized()
Check whether the finalize function has already run.
SmallVector< FinalizationInfo, 8 > FinalizationStack
The finalization stack made up of finalize callbacks currently in-flight, wrapped into FinalizationIn...
LLVM_ABI std::vector< CanonicalLoopInfo * > tileLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops, ArrayRef< Value * > TileSizes)
Tile a loop nest.
LLVM_ABI CallInst * createOMPInteropInit(const LocationDescription &Loc, Value *InteropVar, omp::OMPInteropType InteropType, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_init.
LLVM_ABI Error emitIfClause(Value *Cond, BodyGenCallbackTy ThenGen, BodyGenCallbackTy ElseGen, InsertPointTy AllocaIP={}, ArrayRef< BasicBlock * > DeallocBlocks={})
Emits code for OpenMP 'if' clause using specified BodyGenCallbackTy Here is the logic: if (Cond) { Th...
LLVM_ABI void finalize(Function *Fn=nullptr)
Finalize the underlying module, e.g., by outlining regions.
LLVM_ABI Function * getOrCreateRuntimeFunctionPtr(omp::RuntimeFunction FnID)
void addOutlineInfo(std::unique_ptr< OutlineInfo > &&OI)
Add a new region that will be outlined later.
LLVM_ABI InsertPointTy createTargetInit(const LocationDescription &Loc, const llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs &Attrs)
The omp target interface.
LLVM_ABI InsertPointOrErrorTy createReductions(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< ReductionInfo > ReductionInfos, ArrayRef< bool > IsByRef, bool IsNoWait=false, bool IsTeamsReduction=false)
Generator for 'omp reduction'.
const Triple T
The target triple of the underlying module.
DenseMap< std::pair< Constant *, uint64_t >, Constant * > IdentMap
Map to remember existing ident_t*.
LLVM_ABI CallInst * createOMPFree(const LocationDescription &Loc, Value *Addr, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_free.
LLVM_ABI InsertPointOrErrorTy createReductionsGPU(const LocationDescription &Loc, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< ReductionInfo > ReductionInfos, ArrayRef< bool > IsByRef, bool IsNoWait=false, bool IsTeamsReduction=false, bool IsSPMD=false, ReductionGenCBKind ReductionGenCBKind=ReductionGenCBKind::MLIR, std::optional< omp::GV > GridValue={}, Value *SrcLocInfo=nullptr)
Design of OpenMP reductions on the GPU.
LLVM_ABI FunctionCallee createForStaticInitFunction(unsigned IVSize, bool IVSigned, bool IsGPUDistribute)
Returns __kmpc_for_static_init_* runtime function for the specified size IVSize and sign IVSigned.
LLVM_ABI CallInst * createOMPAlloc(const LocationDescription &Loc, Value *Size, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_alloc.
LLVM_ABI void emitNonContiguousDescriptor(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo, TargetDataInfo &Info)
Emit an array of struct descriptors to be assigned to the offload args.
LLVM_ABI InsertPointOrErrorTy createSection(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB)
Generator for 'omp section'.
LLVM_ABI InsertPointOrErrorTy createTaskgroup(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB)
Generator for the taskgroup construct.
LLVM_ABI InsertPointOrErrorTy createParallel(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB, PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition, Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable)
Generator for 'omp parallel'.
function_ref< InsertPointOrErrorTy(InsertPointTy)> EmitFallbackCallbackTy
Callback function type for functions emitting the host fallback code that is executed when the kernel...
static LLVM_ABI TargetRegionEntryInfo getTargetEntryUniqueInfo(FileIdentifierInfoCallbackTy CallBack, vfs::FileSystem &VFS, StringRef ParentName="")
Creates a unique info for a target entry when provided a filename and line number from.
LLVM_ABI void emitTaskDependency(IRBuilderBase &Builder, Value *Entry, const DependData &Dep)
Store one kmp_depend_info entry at the given Entry pointer.
LLVM_ABI void emitBlock(BasicBlock *BB, Function *CurFn, bool IsFinished=false)
LLVM_ABI Value * getOrCreateThreadID(Value *Ident)
Return the current thread ID.
LLVM_ABI InsertPointOrErrorTy createMaster(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB)
Generator for 'omp master'.
LLVM_ABI InsertPointOrErrorTy createTargetData(const LocationDescription &Loc, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks, Value *DeviceID, Value *IfCond, TargetDataInfo &Info, GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB, omp::RuntimeFunction *MapperFunc=nullptr, function_ref< InsertPointOrErrorTy(InsertPointTy CodeGenIP, BodyGenTy BodyGenType)> BodyGenCB=nullptr, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr, Value *SrcLocInfo=nullptr)
Generator for 'omp target data'.
LLVM_ABI CallInst * createRuntimeFunctionCall(FunctionCallee Callee, ArrayRef< Value * > Args, StringRef Name="")
LLVM_ABI InsertPointOrErrorTy emitKernelLaunch(const LocationDescription &Loc, Value *OutlinedFnID, EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args, Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP)
Generate a target region entry call and host fallback call.
StringMap< GlobalVariable *, BumpPtrAllocator > InternalVars
An ordered map of auto-generated variables to their unique names.
LLVM_ABI InsertPointOrErrorTy createCancellationPoint(const LocationDescription &Loc, omp::Directive CanceledDirective)
Generator for 'omp cancellation point'.
LLVM_ABI CallInst * createOMPAlignedAlloc(const LocationDescription &Loc, Value *Align, Value *Size, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_align_alloc.
LLVM_ABI FunctionCallee createDispatchInitFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_init_* runtime function for the specified size IVSize and sign IVSigned.
LLVM_ABI InsertPointOrErrorTy createScan(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< llvm::Value * > ScanVars, ArrayRef< llvm::Type * > ScanVarsType, bool IsInclusive, ScanInfo *ScanRedInfo)
This directive split and directs the control flow to input phase blocks or scan phase blocks based on...
LLVM_ABI CallInst * createOMPFreeShared(const LocationDescription &Loc, Value *Addr, Value *Size, const Twine &Name=Twine(""))
Create a runtime call for kmpc_free_shared.
LLVM_ABI CallInst * createOMPInteropUse(const LocationDescription &Loc, Value *InteropVar, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_use.
IRBuilder<>::InsertPoint InsertPointTy
Type used throughout for insertion points.
LLVM_ABI GlobalVariable * getOrCreateInternalVariable(Type *Ty, const StringRef &Name, std::optional< unsigned > AddressSpace={})
Gets (if variable with the given name already exist) or creates internal global variable with the spe...
LLVM_ABI GlobalVariable * createOffloadMapnames(SmallVectorImpl< llvm::Constant * > &Names, std::string VarName)
Create the global variable holding the offload names information.
std::forward_list< ScanInfo > ScanInfos
Collection of owned ScanInfo objects that eventually need to be free'd.
static LLVM_ABI void writeTeamsForKernel(const Triple &T, Function &Kernel, int32_t LB, int32_t UB)
LLVM_ABI Value * calculateCanonicalLoopTripCount(const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, const Twine &Name="loop")
Calculate the trip count of a canonical loop.
LLVM_ABI InsertPointOrErrorTy createBarrier(const LocationDescription &Loc, omp::Directive Kind, bool ForceSimpleCall=false, bool CheckCancelFlag=true)
Emitter methods for OpenMP directives.
LLVM_ABI void setCorrectMemberOfFlag(omp::OpenMPOffloadMappingFlags &Flags, omp::OpenMPOffloadMappingFlags MemberOfFlag)
Given an initial flag set, this function modifies it to contain the passed in MemberOfFlag generated ...
LLVM_ABI Error emitOffloadingArraysAndArgs(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info, TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo, CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous=false, bool ForEndCall=false, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr)
Allocates memory for and populates the arrays required for offloading (offload_{baseptrs|ptrs|mappers...
LLVM_ABI Constant * getOrCreateDefaultSrcLocStr(uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the default source location.
LLVM_ABI InsertPointOrErrorTy createCritical(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst)
Generator for 'omp critical'.
LLVM_ABI void createError(const LocationDescription &Loc, bool IsFatal, Value *Message)
Generate a call to the runtime to emit the diagnostic of an OpenMP error directive with at(execution)...
LLVM_ABI void createOffloadEntry(Constant *ID, Constant *Addr, uint64_t Size, int32_t Flags, GlobalValue::LinkageTypes, StringRef Name="")
Creates offloading entry for the provided entry ID ID, address Addr, size Size, and flags Flags.
static LLVM_ABI unsigned getOpenMPDefaultSimdAlign(const Triple &TargetTriple, const StringMap< bool > &Features)
Get the default alignment value for given target.
LLVM_ABI unsigned getFlagMemberOffset()
Get the offset of the OMP_MAP_MEMBER_OF field.
LLVM_ABI InsertPointOrErrorTy applyWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP, bool NeedsBarrier, llvm::omp::ScheduleKind SchedKind=llvm::omp::OMP_SCHEDULE_Default, Value *ChunkSize=nullptr, bool HasSimdModifier=false, bool HasMonotonicModifier=false, bool HasNonmonotonicModifier=false, bool HasOrderedClause=false, omp::WorksharingLoopType LoopType=omp::WorksharingLoopType::ForStaticLoop, bool NoLoop=false, bool HasDistSchedule=false, Value *DistScheduleChunkSize=nullptr)
Modifies the canonical loop to be a workshare loop.
LLVM_ABI InsertPointOrErrorTy createAtomicCapture(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, AtomicOpValue &V, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: — Only Scalar data types V = X; X = X BinOp Expr ,...
LLVM_ABI 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:128
ValueTy lookup(StringRef Key) const
lookup - Return the entry for the specified key, or a default constructed value if no such entry exis...
Definition StringMap.h:249
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
std::string str() const
Get the contents as an std::string.
Definition StringRef.h:222
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
size_t count(char C) const
Return the number of occurrences of C in the string.
Definition StringRef.h:471
bool ends_with(StringRef Suffix) const
Check if this string ends with the given Suffix.
Definition StringRef.h:270
StringRef drop_back(size_t N=1) const
Return a StringRef equal to 'this' but with the last N elements dropped.
Definition StringRef.h:642
Class to represent struct types.
static LLVM_ABI StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition Type.cpp:477
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:683
Type * getElementType(unsigned N) const
Multiway switch.
LLVM_ABI void addCase(ConstantInt *OnVal, BasicBlock *Dest)
Add an entry to the switch instruction.
Analysis pass providing the TargetTransformInfo.
LLVM_ABI Result run(const Function &F, FunctionAnalysisManager &)
Analysis pass providing the TargetLibraryInfo.
Target - Wrapper for Target specific information.
TargetMachine * createTargetMachine(const Triple &TT, StringRef CPU, StringRef Features, const TargetOptions &Options, std::optional< Reloc::Model > RM, std::optional< CodeModel::Model > CM=std::nullopt, CodeGenOptLevel OL=CodeGenOptLevel::Default, bool JIT=false) const
createTargetMachine - Create a target specific machine implementation for the specified Triple.
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
bool isPPC() const
Tests whether the target is PowerPC (32- or 64-bit LE or BE).
Definition Triple.h:1135
bool isX86() const
Tests whether the target is x86 (32- or 64-bit).
Definition Triple.h:1195
bool isWasm() const
Tests whether the target is wasm (32- and 64-bit).
Definition Triple.h:1209
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
LLVM_ABI std::string str() const
Return the twine contents as a std::string.
Definition Twine.cpp:17
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt64Ty(LLVMContext &C)
Definition Type.cpp:310
LLVM_ABI unsigned getIntegerBitWidth() const
LLVM_ABI Type * getStructElementType(unsigned N) const
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:309
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:282
LLVM_ABI unsigned getPointerAddressSpace() const
Get the address space of this pointer or pointer vector type.
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:282
bool isStructTy() const
True if this is an instance of StructType.
Definition Type.h:276
LLVM_ABI unsigned getScalarSizeInBits() const LLVM_READONLY
If this is a vector type, return the getPrimitiveSizeInBits value for the element type.
Definition Type.cpp:232
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition Type.h:186
bool isIntegerTy() const
True if this is an instance of IntegerType.
Definition Type.h:257
static LLVM_ABI IntegerType * getIntNTy(LLVMContext &C, unsigned N)
Definition Type.cpp:313
bool isVoidTy() const
Return true if this is 'void'.
Definition Type.h:141
Unconditional Branch instruction.
static UncondBrInst * Create(BasicBlock *Target, InsertPosition InsertBefore=nullptr)
static LLVM_ABI UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
This function has undefined behavior.
Produce an estimate of the unrolled cost of the specified loop.
Definition UnrollLoop.h:150
LLVM_ABI bool canUnroll(OptimizationRemarkEmitter *ORE=nullptr, const Loop *L=nullptr) const
Whether it is legal to unroll this loop.
uint64_t getRolledLoopSize() const
Definition UnrollLoop.h:174
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
void setOperand(unsigned i, Value *Val)
Definition User.h:212
Value * getOperand(unsigned i) const
Definition User.h:207
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
user_iterator user_begin()
Definition Value.h:402
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
Definition Value.cpp:394
bool hasOneUse() const
Return true if there is exactly one use of this value.
Definition Value.h:439
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
iterator_range< user_iterator > users()
Definition Value.h:426
User * user_back()
Definition Value.h:412
LLVM_ABI Align getPointerAlignment(const DataLayout &DL) const
Returns an alignment of the pointer value.
Definition Value.cpp:993
LLVM_ABI bool hasNUses(unsigned N) const
Return true if this Value has exactly N uses.
Definition Value.cpp:147
LLVM_ABI User * getUniqueUndroppableUser()
Return true if there is exactly one unique user of this value that cannot be dropped (that user can h...
Definition Value.cpp:185
LLVM_ABI const Value * stripPointerCasts() const
Strip off pointer casts, all-zero GEPs and address space casts.
Definition Value.cpp:713
bool use_empty() const
Definition Value.h:346
user_iterator user_end()
Definition Value.h:410
LLVM_ABI bool replaceUsesWithIf(Value *New, llvm::function_ref< bool(Use &U)> ShouldReplace)
Go through the uses list for this definition and make each use point to "V" if the callback ShouldRep...
Definition Value.cpp:561
iterator_range< use_iterator > uses()
Definition Value.h:380
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:319
An efficient, type-erasing, non-owning reference to a callable.
const ParentTy * getParent() const
Definition ilist_node.h:34
self_iterator getIterator()
Definition ilist_node.h:123
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition ilist_node.h:348
A raw_ostream that writes to an SmallVector or SmallString.
StringRef str() const
Return a StringRef for the vector contents.
The virtual file system interface.
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(const Twine &Name, int64_t FileSize=-1, bool RequiresNullTerminator=true, bool IsVolatile=false, bool IsText=true)
This is a convenience method that opens a file, gets its content and then closes the file.
virtual llvm::ErrorOr< Status > status(const Twine &Path)=0
Get the status of the entry at Path, if one exists.
CallInst * Call
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
@ AMDGPU_KERNEL
Used for AMDGPU code object kernels.
@ SPIR_KERNEL
Used for SPIR kernel functions.
@ PTX_Kernel
Call to a PTX kernel. Passes all arguments in parameter space.
@ BasicBlock
Various leaf nodes.
Definition ISDOpcodes.h:81
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
Flag
These should be considered private to the implementation of the MCInstrDesc class.
constexpr StringLiteral MaxNTID("nvvm.maxntid")
constexpr StringLiteral MaxClusterRank("nvvm.maxclusterrank")
initializer< Ty > init(const Ty &Val)
@ User
could "use" a pointer
LLVM_ABI GlobalVariable * emitOffloadingEntry(Module &M, object::OffloadKind Kind, Constant *Addr, StringRef Name, uint64_t Size, uint32_t Flags, uint64_t Data, Constant *AuxAddr=nullptr)
Definition Utility.cpp:105
OpenMPOffloadMappingFlags
Values for bit flags used to specify the mapping type for offloading.
@ OMP_MAP_PTR_AND_OBJ
The element being mapped is a pointer-pointee pair; both the pointer and the pointee should be mapped...
@ OMP_MAP_MEMBER_OF
The 16 MSBs of the flags indicate whether the entry is member of some struct/class.
IdentFlag
IDs for all omp runtime library ident_t flag encodings (see their defintion in openmp/runtime/src/kmp...
RuntimeFunction
IDs for all omp runtime library (RTL) functions.
constexpr const GV & getAMDGPUGridValues()
static constexpr GV SPIRVGridValues
For generic SPIR-V GPUs.
OMPDynGroupprivateFallbackType
The fallback types for the dyn_groupprivate clause.
static constexpr GV NVPTXGridValues
For Nvidia GPUs.
@ OMP_TGT_EXEC_MODE_SPMD_NO_LOOP
Function * Kernel
Summary of a kernel (=entry point for target offloading).
Definition OpenMPOpt.h:21
WorksharingLoopType
A type of worksharing loop construct.
OMPAtomicCompareOp
Atomic compare operations. Currently OpenMP only supports ==, >, and <.
NodeAddr< PhiNode * > Phi
Definition RDFGraph.h:390
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h:315
LLVM_ABI BasicBlock * splitBBWithSuffix(IRBuilderBase &Builder, bool CreateBranch, llvm::Twine Suffix=".split")
Like splitBB, but reuses the current block's name for the new name.
@ Offset
Definition DWP.cpp:578
detail::zippy< detail::zip_shortest, T, U, Args... > zip(T &&t, U &&u, Args &&...args)
zip iterator for two or more iteratable types.
Definition STLExtras.h:830
LLVM_ABI unsigned computeUnrollCount(Loop *L, const TargetTransformInfo &TTI, DominatorTree &DT, LoopInfo *LI, AssumptionCache *AC, ScalarEvolution &SE, const SmallPtrSetImpl< const Value * > &EphValues, OptimizationRemarkEmitter *ORE, unsigned TripCount, unsigned MaxTripCount, bool MaxOrZero, unsigned TripMultiple, const UnrollCostEstimator &UCE, TargetTransformInfo::UnrollingPreferences &UP, TargetTransformInfo::PeelingPreferences &PP)
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1739
hash_code hash_value(const FixedPointSemantics &Val)
LLVM_ABI Expected< std::unique_ptr< Module > > parseBitcodeFile(MemoryBufferRef Buffer, LLVMContext &Context, ParserCallbacks Callbacks={})
Read the specified bitcode file, returning the module.
detail::zippy< detail::zip_first, T, U, Args... > zip_equal(T &&t, U &&u, Args &&...args)
zip iterator that assumes that all iteratees have the same length.
Definition STLExtras.h:840
LLVM_ABI BasicBlock * CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap, const Twine &NameSuffix="", Function *F=nullptr, ClonedCodeInfo *CodeInfo=nullptr, bool MapAtoms=true)
Return a copy of the specified basic block, but without embedding the block into a particular functio...
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition STLExtras.h:2554
unsigned getPointerAddressSpace(const Type *T)
Definition SPIRVUtils.h: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:332
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
LLVM_ABI TargetTransformInfo::PeelingPreferences gatherPeelingPreferences(Loop *L, ScalarEvolution &SE, const TargetTransformInfo &TTI, std::optional< bool > UserAllowPeeling, std::optional< bool > UserAllowProfileBasedPeeling, bool UnrollingSpecficValues=false)
LLVM_ABI void SplitBlockAndInsertIfThenElse(Value *Cond, BasicBlock::iterator SplitBefore, Instruction **ThenTerm, Instruction **ElseTerm, MDNode *BranchWeights=nullptr, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr)
SplitBlockAndInsertIfThenElse is similar to SplitBlockAndInsertIfThen, but also creates the ElseBlock...
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1753
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
CodeGenOptLevel
Code generation optimization level.
Definition CodeGen.h:149
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition Format.h: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:395
TargetTransformInfo TTI
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
LLVM_ABI bool MergeBlockIntoPredecessor(BasicBlock *BB, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, MemoryDependenceResults *MemDep=nullptr, bool PredecessorWithTwoSuccessors=false, DominatorTree *DT=nullptr)
Attempts to merge a block into its predecessor, if possible.
@ Mul
Product of integers.
@ Add
Sum of integers.
LLVM_ABI BasicBlock * SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt, DominatorTree *DT, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, const Twine &BBName="")
Split the specified block at the specified instruction.
IntPtrTy
Definition InstrProf.h:82
DWARFExpression::Operation Op
LLVM_ABI void remapInstructionsInBlocks(ArrayRef< BasicBlock * > Blocks, ValueToValueMapTy &VMap)
Remaps instructions in Blocks using the mapping in VMap.
ArrayRef(const T &OneElt) -> ArrayRef< T >
OutputIt copy(R &&Range, OutputIt Out)
Definition STLExtras.h:1885
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.
ArrayRef< Value * > NumThreads
The number of threads.
TargetDataRTArgs RTArgs
Arguments passed to the runtime library.
Value * NumIterations
The number of iterations.
Value * DynCGroupMem
The size of the dynamic shared memory.
unsigned NumTargetItems
Number of arguments passed to the runtime library.
bool StrictBlocksAndThreads
True if the kernel strictly requires the number of blocks and threads above to run.
bool HasNoWait
True if the kernel has 'no wait' clause.
ArrayRef< Value * > NumTeams
The number of teams.
omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback
The fallback mechanism for the shared memory.
Container to pass the default attributes with which a kernel must be launched, used to set kernel att...
Container to pass LLVM IR runtime values or constants related to the number of teams and threads with...
Value * DeviceID
Device ID value used in the kernel launch.
Value * MaxThreads
'parallel' construct 'num_threads' clause value, if present and it is an SPMD kernel.
Value * LoopTripCount
Total number of iterations of the SPMD or Generic-SPMD kernel or null if it is a generic kernel.
Data structure to contain the information needed to uniquely identify a target entry.
static LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, StringRef ParentName, unsigned DeviceID, unsigned FileID, unsigned Line, unsigned Count)
static constexpr const char * KernelNamePrefix
The prefix used for kernel names.
static LLVM_ABI const Target * lookupTarget(const Triple &TheTriple, std::string &Error)
lookupTarget - Lookup a target based on a target triple.
Parameters that control the generic loop unrolling transformation.
unsigned Threshold
The cost threshold for the unrolled loop.
bool Force
Apply loop unroll on any kind of loop (mainly to loops that fail runtime unrolling).
unsigned PartialOptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size, like OptSizeThreshold,...
unsigned PartialThreshold
The cost threshold for the unrolled loop, like Threshold, but used for partial/runtime unrolling (set...
unsigned OptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size (set to UINT_MAX to disable).
Defines various target-specific GPU grid values that must be consistent between host RTL (plugin),...